Merge autoland to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Mon, 15 Apr 2019 18:23:37 +0300
changeset 469488 16d953cca41483b114d70a3132fbcfe60755708f
parent 469470 9f0f38c38ccce7c9656c77205b16dfb35208df2d (current diff)
parent 469487 002bee1cab24df7550929cde1059029eebfee71d (diff)
child 469506 85aec9a8e154758a7f6322df7b788ac8f484071a
child 469600 a2b89a27e8eb6881a1f29731c9ebc5cc1edc386d
push id35872
push userncsoregi@mozilla.com
push dateMon, 15 Apr 2019 15:24:06 +0000
treeherdermozilla-central@16d953cca414 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
16d953cca414 / 68.0a1 / 20190415152406 / files
nightly linux64
16d953cca414 / 68.0a1 / 20190415152406 / files
nightly mac
16d953cca414 / 68.0a1 / 20190415152406 / files
nightly win32
16d953cca414 / 68.0a1 / 20190415152406 / files
nightly win64
16d953cca414 / 68.0a1 / 20190415152406 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
third_party/rust/target-lexicon/.rustfmt.toml
third_party/rust/target-lexicon/.travis.yml
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -17,15 +17,15 @@ git = "https://github.com/froydnj/winapi
 branch = "aarch64"
 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/target-lexicon"]
-git = "https://github.com/glandium/target-lexicon"
-branch = "thumbv7neon-v0.2"
+[source."https://github.com/CraneStation/Cranelift"]
+git = "https://github.com/CraneStation/Cranelift"
+rev = "542d799dd7a3b2cc15b91eefdcd85cace8fe5cee"
 replace-with = "vendored-sources"
 
 [source.vendored-sources]
 directory = '@top_srcdir@/third_party/rust'
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -163,21 +163,21 @@ 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.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-wasm 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
+ "cranelift-wasm 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
  "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.2.0 (git+https://github.com/glandium/target-lexicon?branch=thumbv7neon-v0.2)",
+ "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "base64"
 version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -563,72 +563,72 @@ name = "cose-c"
 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.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cranelift-entity 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
+version = "0.30.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee#542d799dd7a3b2cc15b91eefdcd85cace8fe5cee"
+dependencies = [
+ "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cranelift-bforest 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-codegen-meta 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-entity 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
+version = "0.30.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee#542d799dd7a3b2cc15b91eefdcd85cace8fe5cee"
+dependencies = [
+ "cranelift-bforest 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
+ "cranelift-codegen-meta 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
+ "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
  "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.2.0 (git+https://github.com/glandium/target-lexicon?branch=thumbv7neon-v0.2)",
+ "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cranelift-entity 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
+version = "0.30.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee#542d799dd7a3b2cc15b91eefdcd85cace8fe5cee"
+dependencies = [
+ "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
+version = "0.30.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee#542d799dd7a3b2cc15b91eefdcd85cace8fe5cee"
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cranelift-codegen 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
+version = "0.30.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee#542d799dd7a3b2cc15b91eefdcd85cace8fe5cee"
+dependencies = [
+ "cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "target-lexicon 0.2.0 (git+https://github.com/glandium/target-lexicon?branch=thumbv7neon-v0.2)",
+ "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-wasm"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
+version = "0.30.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee#542d799dd7a3b2cc15b91eefdcd85cace8fe5cee"
 dependencies = [
  "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-codegen 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-entity 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-frontend 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
+ "cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
+ "cranelift-frontend 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)",
  "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.23.0 (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"
 version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2773,18 +2773,18 @@ dependencies = [
  "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "target-lexicon"
-version = "0.2.0"
-source = "git+https://github.com/glandium/target-lexicon?branch=thumbv7neon-v0.2#b2d4b34509abb3e12b1c93d19b8593d02ddeed76"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "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)",
  "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "tempdir"
@@ -3198,17 +3198,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "wasmparser"
-version = "0.23.0"
+version = "0.29.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "webdriver"
 version = "0.39.0"
 dependencies = [
  "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3490,22 +3490,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.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a3a25dfe4a54449df63d592f2f6346a80350ac835d4be4bacb73c20b034ef763"
-"checksum cranelift-codegen 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6c3b15a20577e6c823475953a5e25e758d9d3a3148a937d686c09460e5f2f4a0"
-"checksum cranelift-codegen-meta 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e60ce3551e8172c966fbc6d9bfb90111d5d1cf37306c37dd7527467afe317d1"
-"checksum cranelift-entity 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4e124e09cb7dc85fbe2162420aebbe8e9e3b8f9210901be7867416c5beec8226"
-"checksum cranelift-frontend 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2833c6e1a93c524ce0c2ab31266cdc84d38c906349f79f19378a5e5995727b23"
-"checksum cranelift-wasm 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e75efb45cd8d8700b4bdc225f0077bc7c615f84a5807ce001d59b4da48d85573"
+"checksum cranelift-bforest 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)" = "<none>"
+"checksum cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)" = "<none>"
+"checksum cranelift-codegen-meta 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)" = "<none>"
+"checksum cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)" = "<none>"
+"checksum cranelift-frontend 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)" = "<none>"
+"checksum cranelift-wasm 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=542d799dd7a3b2cc15b91eefdcd85cace8fe5cee)" = "<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"
@@ -3686,17 +3686,17 @@ dependencies = [
 "checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423"
 "checksum string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da"
 "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
 "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
 "checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
 "checksum syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4b5274d4a0a3d2749d5c158dc64d3403e60554dc61194648787ada5212473d"
 "checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2"
 "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
-"checksum target-lexicon 0.2.0 (git+https://github.com/glandium/target-lexicon?branch=thumbv7neon-v0.2)" = "<none>"
+"checksum target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b0ab4982b8945c35cc1c46a83a9094c414f6828a099ce5dcaa8ee2b04642dcb"
 "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
 "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
 "checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
 "checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83"
 "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
 "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
 "checksum thin-slice 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
 "checksum thin-vec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "73fdf4b84c65a85168477b7fb6c498e0716bc9487fba24623389ea7f51708044"
@@ -3731,17 +3731,17 @@ dependencies = [
 "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
 "checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363"
 "checksum uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dab5c5526c5caa3d106653401a267fed923e7046f35895ffcb5ca42db64942e6"
 "checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
 "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
 "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
 "checksum walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63636bd0eb3d00ccb8b9036381b526efac53caf112b7783b730ab3f8e44da369"
 "checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3"
-"checksum wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5e01c420bc7d36e778bd242e1167b079562ba8b34087122cc9057187026d060"
+"checksum wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)" = "981a8797cf89762e0233ec45fae731cb79a4dfaee12d9f0fe6cee01e4ac58d00"
 "checksum webidl 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0f807f7488d680893f7188aa09d7672a3a0a8461975a098a2edf0a52e3fee29"
 "checksum which 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4be6cfa54dab45266e98b5d7be2f8ce959ddd49abd141a05d52dce4b07f803bb"
 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
 "checksum winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)" = "<none>"
 "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
 "checksum winapi-i686-pc-windows-gnu 0.4.0 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)" = "<none>"
 "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)" = "<none>"
 "checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767"
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -56,9 +56,10 @@ debug-assertions = false
 panic = "abort"
 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" }
-target-lexicon = { git = "https://github.com/glandium/target-lexicon", branch = "thumbv7neon-v0.2" }
+cranelift-codegen = { git = "https://github.com/CraneStation/Cranelift", rev = "542d799dd7a3b2cc15b91eefdcd85cace8fe5cee" }
+cranelift-wasm = { git = "https://github.com/CraneStation/Cranelift", rev = "542d799dd7a3b2cc15b91eefdcd85cace8fe5cee" }
--- a/browser/base/content/test/sanitize/browser_cookiePermission_subDomains.js
+++ b/browser/base/content/test/sanitize/browser_cookiePermission_subDomains.js
@@ -42,17 +42,67 @@ add_task(async function subDomains() {
   ok(await checkIndexedDB(uriB.host, {}), "We have IDB for URI: " + uriB.host);
 
   // Cleaning up
   await Sanitizer.runSanitizeOnShutdown();
 
   // Check again
   ok(!(await checkCookie(uriA.host, {})), "We should not have cookies for URI: " + uriA.host);
   ok(!(await checkIndexedDB(uriA.host, {})), "We should not have IDB for URI: " + uriA.host);
-
-  // Note that cookies are stored per base domain...
-  ok(!(await checkCookie(uriB.host, {})), "We should not have cookies for URI: " + uriB.host);
+  ok(await checkCookie(uriB.host, {}), "We should have cookies for URI: " + uriB.host);
   ok(await checkIndexedDB(uriB.host, {}), "We should have IDB for URI: " + uriB.host);
 
   // Cleaning up permissions
   Services.perms.remove(uriA, "cookie");
   Services.perms.remove(uriB, "cookie");
 });
+
+// session only cookie life-time, 2 domains (mozilla.org, www.mozilla.org),
+// only the latter has a cookie permission.
+add_task(async function subDomains() {
+  info("Test subdomains and custom setting with cookieBehavior == 2");
+
+  // Let's clean up all the data.
+  await new Promise(resolve => {
+    Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, resolve);
+  });
+
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["network.cookie.lifetimePolicy", Ci.nsICookieService.ACCEPT_SESSION ],
+    ["browser.sanitizer.loglevel", "All"],
+  ]});
+
+  // Domains and data
+  let uriA = Services.io.newURI("https://sub.mozilla.org");
+  Services.perms.add(uriA, "cookie", Ci.nsICookiePermission.ACCESS_ALLOW);
+
+  Services.cookies.add(uriA.host, "/test", "a", "b",
+    false, false, false, Date.now() + 24000 * 60 * 60, {},
+    Ci.nsICookie2.SAMESITE_UNSET);
+
+  await createIndexedDB(uriA.host, {});
+
+  let uriB = Services.io.newURI("https://www.mozilla.org");
+
+  Services.cookies.add(uriB.host, "/test", "c", "d",
+    false, false, false, Date.now() + 24000 * 60 * 60, {},
+    Ci.nsICookie2.SAMESITE_UNSET);
+
+  await createIndexedDB(uriB.host, {});
+
+  // Check
+  ok(await checkCookie(uriA.host, {}), "We have cookies for URI: " + uriA.host);
+  ok(await checkIndexedDB(uriA.host, {}), "We have IDB for URI: " + uriA.host);
+  ok(await checkCookie(uriB.host, {}), "We have cookies for URI: " + uriB.host);
+  ok(await checkIndexedDB(uriB.host, {}), "We have IDB for URI: " + uriB.host);
+
+  // Cleaning up
+  await Sanitizer.runSanitizeOnShutdown();
+
+  // Check again
+  ok(await checkCookie(uriA.host, {}), "We should have cookies for URI: " + uriA.host);
+  ok(await checkIndexedDB(uriA.host, {}), "We should have IDB for URI: " + uriA.host);
+  ok(!await checkCookie(uriB.host, {}), "We should not have cookies for URI: " + uriB.host);
+  ok(!await checkIndexedDB(uriB.host, {}), "We should not have IDB for URI: " + uriB.host);
+
+  // Cleaning up permissions
+  Services.perms.remove(uriA, "cookie");
+});
--- a/caps/OriginAttributes.cpp
+++ b/caps/OriginAttributes.cpp
@@ -77,31 +77,42 @@ void OriginAttributes::SetFirstPartyDoma
       mFirstPartyDomain.AppendLiteral("]");
     } else {
       mFirstPartyDomain = NS_ConvertUTF8toUTF16(ipAddr);
     }
 
     return;
   }
 
+  // Saving isInsufficientDomainLevels before rv is overwritten.
+  bool isInsufficientDomainLevels = (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS);
   nsAutoCString scheme;
   rv = aURI->GetScheme(scheme);
   NS_ENSURE_SUCCESS_VOID(rv);
   if (scheme.EqualsLiteral("about")) {
     mFirstPartyDomain.AssignLiteral(ABOUT_URI_FIRST_PARTY_DOMAIN);
     return;
   }
 
   nsCOMPtr<nsIPrincipal> blobPrincipal;
   if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
           aURI, getter_AddRefs(blobPrincipal))) {
     MOZ_ASSERT(blobPrincipal);
     mFirstPartyDomain = blobPrincipal->OriginAttributesRef().mFirstPartyDomain;
     return;
   }
+
+  if (isInsufficientDomainLevels) {
+    nsAutoCString publicSuffix;
+    rv = tldService->GetPublicSuffix(aURI, publicSuffix);
+    if (NS_SUCCEEDED(rv)) {
+      mFirstPartyDomain = NS_ConvertUTF8toUTF16(publicSuffix);
+    }
+    return;
+  }
 }
 
 void OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument,
                                            const nsACString& aDomain) {
   bool isFirstPartyEnabled = IsFirstPartyEnabled();
 
   // If the pref is off or this is not a top level load, bail out.
   if (!isFirstPartyEnabled || !aIsTopLevelDocument) {
--- a/caps/tests/gtest/TestOriginAttributes.cpp
+++ b/caps/tests/gtest/TestOriginAttributes.cpp
@@ -1,27 +1,38 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "gtest/gtest.h"
 #include "mozilla/BasePrincipal.h"
+#include "mozilla/Preferences.h"
+#include "nsNetUtil.h"
 
 using mozilla::OriginAttributes;
+using mozilla::Preferences;
 
 static void TestSuffix(const OriginAttributes& attrs) {
   nsAutoCString suffix;
   attrs.CreateSuffix(suffix);
 
   OriginAttributes attrsFromSuffix;
   bool success = attrsFromSuffix.PopulateFromSuffix(suffix);
   EXPECT_TRUE(success);
 
   EXPECT_EQ(attrs, attrsFromSuffix);
 }
 
+static void TestFPD(const nsAString &spec, const nsAString &fpd) {
+  OriginAttributes attrs;
+  nsCOMPtr<nsIURI> url;
+  ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
+  attrs.SetFirstPartyDomain(true, url);
+  EXPECT_TRUE(attrs.mFirstPartyDomain.Equals(fpd));
+}
+
 TEST(OriginAttributes, Suffix_default)
 {
   OriginAttributes attrs;
   TestSuffix(attrs);
 }
 
 TEST(OriginAttributes, Suffix_appId_inIsolatedMozBrowser)
 {
@@ -29,8 +40,26 @@ TEST(OriginAttributes, Suffix_appId_inIs
   TestSuffix(attrs);
 }
 
 TEST(OriginAttributes, Suffix_maxAppId_inIsolatedMozBrowser)
 {
   OriginAttributes attrs(4294967295, true);
   TestSuffix(attrs);
 }
+
+TEST(OriginAttributes, FirstPartyDomain_default)
+{
+  static const char prefKey[] = "privacy.firstparty.isolate";
+  bool oldPref = Preferences::GetBool(prefKey);
+  Preferences::SetBool(prefKey, true);
+  TestFPD(NS_LITERAL_STRING("http://www.example.com"),
+          NS_LITERAL_STRING("example.com"));
+  TestFPD(NS_LITERAL_STRING("http://s3.amazonaws.com"),
+          NS_LITERAL_STRING("s3.amazonaws.com"));
+  TestFPD(NS_LITERAL_STRING("http://com"), NS_LITERAL_STRING("com"));
+  TestFPD(NS_LITERAL_STRING("http://.com"), NS_LITERAL_STRING(""));
+  TestFPD(NS_LITERAL_STRING("http://..com"), NS_LITERAL_STRING(""));
+  TestFPD(NS_LITERAL_STRING("http://127.0.0.1"),
+          NS_LITERAL_STRING("127.0.0.1"));
+  TestFPD(NS_LITERAL_STRING("http://[::1]"), NS_LITERAL_STRING("[::1]"));
+  Preferences::SetBool(prefKey, oldPref);
+}
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -2386,16 +2386,21 @@ void Document::FillStyleSetDocumentSheet
   AppendSheetsToStyleSet(mStyleSet.get(), mAdditionalSheets[eAuthorSheet],
                          SheetType::Doc);
 }
 
 void Document::CompatibilityModeChanged() {
   MOZ_ASSERT(IsHTMLOrXHTML());
   CSSLoader()->SetCompatibilityMode(mCompatMode);
   mStyleSet->CompatibilityModeChanged();
+  if (PresShell* presShell = GetPresShell()) {
+    // Selectors may have become case-sensitive / case-insensitive, the stylist
+    // has already performed the relevant invalidation.
+    presShell->EnsureStyleFlush();
+  }
   if (!mStyleSetFilled) {
     MOZ_ASSERT(!mQuirkSheetAdded);
     return;
   }
   if (mQuirkSheetAdded == NeedsQuirksSheet()) {
     return;
   }
   auto cache = nsLayoutStylesheetCache::Singleton();
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -159,17 +159,17 @@ static const uint32_t STALL_MS = 3000;
 static const double MIN_PLAYBACKRATE = 1.0 / 16;
 // Maximum playbackRate for a media
 static const double MAX_PLAYBACKRATE = 16.0;
 // These are the limits beyonds which SoundTouch does not perform too well and
 // when speech is hard to understand anyway. Threshold above which audio is
 // muted
 static const double THRESHOLD_HIGH_PLAYBACKRATE_AUDIO = 4.0;
 // Threshold under which audio is muted
-static const double THRESHOLD_LOW_PLAYBACKRATE_AUDIO = 0.5;
+static const double THRESHOLD_LOW_PLAYBACKRATE_AUDIO = 0.25;
 
 static double ClampPlaybackRate(double aPlaybackRate) {
   MOZ_ASSERT(aPlaybackRate >= 0.0);
 
   if (aPlaybackRate == 0.0) {
     return aPlaybackRate;
   }
   if (aPlaybackRate < MIN_PLAYBACKRATE) {
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -772,16 +772,40 @@ static void AssertSystemPrincipalMustNot
   nsCOMPtr<nsIURI> finalURI;
   NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
   // bail out, if URL isn't pointing to remote resource
   if (!nsContentUtils::SchemeIs(finalURI, "http") &&
       !nsContentUtils::SchemeIs(finalURI, "https") &&
       !nsContentUtils::SchemeIs(finalURI, "ftp")) {
     return;
   }
+
+  // FIXME The discovery feature in about:addons uses the SystemPrincpal.
+  // We should remove this exception with bug 1544011.
+  static nsAutoCString sDiscoveryPrePath;
+  static bool recvdPrefValue = false;
+  if (!recvdPrefValue) {
+    nsAutoCString discoveryURLString;
+    Preferences::GetCString("extensions.webservice.discoverURL",
+                            discoveryURLString);
+    // discoverURL is by default suffixed with parameters in path like
+    // /%LOCALE%/ so, we use the prePath for comparison
+    nsCOMPtr<nsIURI> discoveryURL;
+    NS_NewURI(getter_AddRefs(discoveryURL), discoveryURLString);
+    if (discoveryURL) {
+      discoveryURL->GetPrePath(sDiscoveryPrePath);
+    }
+    recvdPrefValue = true;
+  }
+  nsAutoCString requestedPrePath;
+  finalURI->GetPrePath(requestedPrePath);
+  if (requestedPrePath.Equals(sDiscoveryPrePath)) {
+    return;
+  }
+
   if (xpc::AreNonLocalConnectionsDisabled()) {
     bool disallowSystemPrincipalRemoteDocuments = Preferences::GetBool(
         "security.disallow_non_local_systemprincipal_in_tests");
     if (disallowSystemPrincipalRemoteDocuments) {
       // our own mochitest needs NS_ASSERTION instead of MOZ_ASSERT
       NS_ASSERTION(false, "SystemPrincipal must not load remote documents.");
       return;
     }
--- a/editor/libeditor/EditorCommands.cpp
+++ b/editor/libeditor/EditorCommands.cpp
@@ -38,16 +38,18 @@ namespace mozilla {
 EditorCommandBase::EditorCommandBase() {}
 
 NS_IMPL_ISUPPORTS(EditorCommandBase, nsIControllerCommand)
 
 /******************************************************************************
  * mozilla::UndoCommand
  ******************************************************************************/
 
+StaticRefPtr<UndoCommand> UndoCommand::sInstance;
+
 NS_IMETHODIMP
 UndoCommand::IsCommandEnabled(const char* aCommandName,
                               nsISupports* aCommandRefCon, bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   *aIsEnabled = false;
@@ -92,16 +94,18 @@ UndoCommand::GetCommandStateParams(const
   IsCommandEnabled(aCommandName, aCommandRefCon, &canUndo);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, canUndo);
 }
 
 /******************************************************************************
  * mozilla::RedoCommand
  ******************************************************************************/
 
+StaticRefPtr<RedoCommand> RedoCommand::sInstance;
+
 NS_IMETHODIMP
 RedoCommand::IsCommandEnabled(const char* aCommandName,
                               nsISupports* aCommandRefCon, bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   *aIsEnabled = false;
@@ -146,16 +150,18 @@ RedoCommand::GetCommandStateParams(const
   IsCommandEnabled(aCommandName, aCommandRefCon, &canUndo);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, canUndo);
 }
 
 /******************************************************************************
  * mozilla::CutCommand
  ******************************************************************************/
 
+StaticRefPtr<CutCommand> CutCommand::sInstance;
+
 NS_IMETHODIMP
 CutCommand::IsCommandEnabled(const char* aCommandName,
                              nsISupports* aCommandRefCon, bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   *aIsEnabled = false;
@@ -197,16 +203,18 @@ CutCommand::GetCommandStateParams(const 
   IsCommandEnabled(aCommandName, aCommandRefCon, &canUndo);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, canUndo);
 }
 
 /******************************************************************************
  * mozilla::CutOrDeleteCommand
  ******************************************************************************/
 
+StaticRefPtr<CutOrDeleteCommand> CutOrDeleteCommand::sInstance;
+
 NS_IMETHODIMP
 CutOrDeleteCommand::IsCommandEnabled(const char* aCommandName,
                                      nsISupports* aCommandRefCon,
                                      bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
@@ -256,16 +264,18 @@ CutOrDeleteCommand::GetCommandStateParam
   IsCommandEnabled(aCommandName, aCommandRefCon, &canUndo);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, canUndo);
 }
 
 /******************************************************************************
  * mozilla::CopyCommand
  ******************************************************************************/
 
+StaticRefPtr<CopyCommand> CopyCommand::sInstance;
+
 NS_IMETHODIMP
 CopyCommand::IsCommandEnabled(const char* aCommandName,
                               nsISupports* aCommandRefCon, bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
   if (!editor) {
@@ -303,16 +313,18 @@ CopyCommand::GetCommandStateParams(const
   IsCommandEnabled(aCommandName, aCommandRefCon, &canUndo);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, canUndo);
 }
 
 /******************************************************************************
  * mozilla::CopyOrDeleteCommand
  ******************************************************************************/
 
+StaticRefPtr<CopyOrDeleteCommand> CopyOrDeleteCommand::sInstance;
+
 NS_IMETHODIMP
 CopyOrDeleteCommand::IsCommandEnabled(const char* aCommandName,
                                       nsISupports* aCommandRefCon,
                                       bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
 
@@ -363,16 +375,18 @@ CopyOrDeleteCommand::GetCommandStatePara
   IsCommandEnabled(aCommandName, aCommandRefCon, &canUndo);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, canUndo);
 }
 
 /******************************************************************************
  * mozilla::PasteCommand
  ******************************************************************************/
 
+StaticRefPtr<PasteCommand> PasteCommand::sInstance;
+
 NS_IMETHODIMP
 PasteCommand::IsCommandEnabled(const char* aCommandName,
                                nsISupports* aCommandRefCon, bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   *aIsEnabled = false;
@@ -417,16 +431,18 @@ PasteCommand::GetCommandStateParams(cons
   IsCommandEnabled(aCommandName, aCommandRefCon, &canUndo);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, canUndo);
 }
 
 /******************************************************************************
  * mozilla::PasteTransferableCommand
  ******************************************************************************/
 
+StaticRefPtr<PasteTransferableCommand> PasteTransferableCommand::sInstance;
+
 NS_IMETHODIMP
 PasteTransferableCommand::IsCommandEnabled(const char* aCommandName,
                                            nsISupports* aCommandRefCon,
                                            bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
 
@@ -509,16 +525,18 @@ PasteTransferableCommand::GetCommandStat
   return aParams->AsCommandParams()->SetBool(
       STATE_ENABLED, textEditor->CanPasteTransferable(trans));
 }
 
 /******************************************************************************
  * mozilla::SwitchTextDirectionCommand
  ******************************************************************************/
 
+StaticRefPtr<SwitchTextDirectionCommand> SwitchTextDirectionCommand::sInstance;
+
 NS_IMETHODIMP
 SwitchTextDirectionCommand::IsCommandEnabled(const char* aCommandName,
                                              nsISupports* aCommandRefCon,
                                              bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
@@ -561,16 +579,18 @@ SwitchTextDirectionCommand::GetCommandSt
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED,
                                              canSwitchTextDirection);
 }
 
 /******************************************************************************
  * mozilla::DeleteCommand
  ******************************************************************************/
 
+StaticRefPtr<DeleteCommand> DeleteCommand::sInstance;
+
 NS_IMETHODIMP
 DeleteCommand::IsCommandEnabled(const char* aCommandName,
                                 nsISupports* aCommandRefCon, bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   *aIsEnabled = false;
@@ -653,16 +673,18 @@ DeleteCommand::GetCommandStateParams(con
   IsCommandEnabled(aCommandName, aCommandRefCon, &canUndo);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, canUndo);
 }
 
 /******************************************************************************
  * mozilla::SelectAllCommand
  ******************************************************************************/
 
+StaticRefPtr<SelectAllCommand> SelectAllCommand::sInstance;
+
 NS_IMETHODIMP
 SelectAllCommand::IsCommandEnabled(const char* aCommandName,
                                    nsISupports* aCommandRefCon,
                                    bool* aIsEnabled) {
   NS_ENSURE_ARG_POINTER(aIsEnabled);
 
   nsresult rv = NS_OK;
   // You can always select all, unless the selection is editable,
@@ -713,16 +735,18 @@ SelectAllCommand::GetCommandStateParams(
   IsCommandEnabled(aCommandName, aCommandRefCon, &canUndo);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, canUndo);
 }
 
 /******************************************************************************
  * mozilla::SelectionMoveCommands
  ******************************************************************************/
 
+StaticRefPtr<SelectionMoveCommands> SelectionMoveCommands::sInstance;
+
 NS_IMETHODIMP
 SelectionMoveCommands::IsCommandEnabled(const char* aCommandName,
                                         nsISupports* aCommandRefCon,
                                         bool* aIsEnabled) {
   NS_ENSURE_ARG_POINTER(aIsEnabled);
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
   if (!editor) {
     *aIsEnabled = false;
@@ -857,16 +881,18 @@ SelectionMoveCommands::GetCommandStatePa
   IsCommandEnabled(aCommandName, aCommandRefCon, &canUndo);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, canUndo);
 }
 
 /******************************************************************************
  * mozilla::InsertPlaintextCommand
  ******************************************************************************/
 
+StaticRefPtr<InsertPlaintextCommand> InsertPlaintextCommand::sInstance;
+
 NS_IMETHODIMP
 InsertPlaintextCommand::IsCommandEnabled(const char* aCommandName,
                                          nsISupports* aCommandRefCon,
                                          bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
@@ -946,16 +972,18 @@ InsertPlaintextCommand::GetCommandStateP
   IsCommandEnabled(aCommandName, aCommandRefCon, &aIsEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, aIsEnabled);
 }
 
 /******************************************************************************
  * mozilla::InsertParagraphCommand
  ******************************************************************************/
 
+StaticRefPtr<InsertParagraphCommand> InsertParagraphCommand::sInstance;
+
 NS_IMETHODIMP
 InsertParagraphCommand::IsCommandEnabled(const char* aCommandName,
                                          nsISupports* aCommandRefCon,
                                          bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
@@ -1004,16 +1032,18 @@ InsertParagraphCommand::GetCommandStateP
   IsCommandEnabled(aCommandName, aCommandRefCon, &aIsEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, aIsEnabled);
 }
 
 /******************************************************************************
  * mozilla::InsertLineBreakCommand
  ******************************************************************************/
 
+StaticRefPtr<InsertLineBreakCommand> InsertLineBreakCommand::sInstance;
+
 NS_IMETHODIMP
 InsertLineBreakCommand::IsCommandEnabled(const char* aCommandName,
                                          nsISupports* aCommandRefCon,
                                          bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
@@ -1062,16 +1092,18 @@ InsertLineBreakCommand::GetCommandStateP
   IsCommandEnabled(aCommandName, aCommandRefCon, &aIsEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, aIsEnabled);
 }
 
 /******************************************************************************
  * mozilla::PasteQuotationCommand
  ******************************************************************************/
 
+StaticRefPtr<PasteQuotationCommand> PasteQuotationCommand::sInstance;
+
 NS_IMETHODIMP
 PasteQuotationCommand::IsCommandEnabled(const char* aCommandName,
                                         nsISupports* aCommandRefCon,
                                         bool* aIsEnabled) {
   if (NS_WARN_IF(!aIsEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
 
--- a/editor/libeditor/EditorCommands.h
+++ b/editor/libeditor/EditorCommands.h
@@ -1,46 +1,45 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef EditorCommands_h_
 #define EditorCommands_h_
 
+#include "mozilla/StaticPtr.h"
 #include "nsIControllerCommand.h"
 #include "nsISupportsImpl.h"
 #include "nscore.h"
 
 class nsICommandParams;
 class nsISupports;
 
 namespace mozilla {
 
 /**
- * This is a virtual base class for commands registered with the editor
- * controller.  Note that such commands can be shared by more than on editor
- * instance, so MUST be stateless. Any state must be stored via the refCon
- * (an nsIEditor).
+ * This is a base class for commands registered with the editor controller.
+ * Note that such commands are designed as singleton classes.  So, MUST be
+ * stateless. Any state must be stored via the refCon (an nsIEditor).
  */
 
 class EditorCommandBase : public nsIControllerCommand {
  public:
-  EditorCommandBase();
-
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD IsCommandEnabled(const char* aCommandName,
                               nsISupports* aCommandRefCon,
                               bool* aIsEnabled) override = 0;
   MOZ_CAN_RUN_SCRIPT
   NS_IMETHOD DoCommand(const char* aCommandName,
                        nsISupports* aCommandRefCon) override = 0;
 
  protected:
+  EditorCommandBase();
   virtual ~EditorCommandBase() {}
 };
 
 #define NS_DECL_EDITOR_COMMAND(_cmd)                                        \
   class _cmd final : public EditorCommandBase {                             \
    public:                                                                  \
     NS_IMETHOD IsCommandEnabled(const char* aCommandName,                   \
                                 nsISupports* aCommandRefCon,                \
@@ -50,16 +49,31 @@ class EditorCommandBase : public nsICont
                          nsISupports* aCommandRefCon) override;             \
     MOZ_CAN_RUN_SCRIPT                                                      \
     NS_IMETHOD DoCommandParams(const char* aCommandName,                    \
                                nsICommandParams* aParams,                   \
                                nsISupports* aCommandRefCon) override;       \
     NS_IMETHOD GetCommandStateParams(const char* aCommandName,              \
                                      nsICommandParams* aParams,             \
                                      nsISupports* aCommandRefCon) override; \
+    static _cmd* GetInstance() {                                            \
+      if (!sInstance) {                                                     \
+        sInstance = new _cmd();                                             \
+      }                                                                     \
+      return sInstance;                                                     \
+    }                                                                       \
+                                                                            \
+    static void Shutdown() { sInstance = nullptr; }                         \
+                                                                            \
+   protected:                                                               \
+    _cmd() = default;                                                       \
+    virtual ~_cmd() = default;                                              \
+                                                                            \
+   private:                                                                 \
+    static StaticRefPtr<_cmd> sInstance;                                    \
   };
 
 // basic editor commands
 NS_DECL_EDITOR_COMMAND(UndoCommand)
 NS_DECL_EDITOR_COMMAND(RedoCommand)
 
 NS_DECL_EDITOR_COMMAND(CutCommand)
 NS_DECL_EDITOR_COMMAND(CutOrDeleteCommand)
--- a/editor/libeditor/EditorController.cpp
+++ b/editor/libeditor/EditorController.cpp
@@ -10,126 +10,131 @@
 #include "nsControllerCommandTable.h"
 #include "nsDebug.h"
 #include "nsError.h"
 
 class nsIControllerCommand;
 
 namespace mozilla {
 
-#define NS_REGISTER_ONE_COMMAND(_cmdClass, _cmdName)           \
-  {                                                            \
-    _cmdClass* theCmd = new _cmdClass();                       \
-    NS_ENSURE_TRUE(theCmd, NS_ERROR_OUT_OF_MEMORY);            \
-    aCommandTable->RegisterCommand(                            \
-        _cmdName, static_cast<nsIControllerCommand*>(theCmd)); \
-  }
-
-#define NS_REGISTER_FIRST_COMMAND(_cmdClass, _cmdName) \
-  {                                                    \
-    _cmdClass* theCmd = new _cmdClass();               \
-    NS_ENSURE_TRUE(theCmd, NS_ERROR_OUT_OF_MEMORY);    \
-    aCommandTable->RegisterCommand(                    \
-        _cmdName, static_cast<nsIControllerCommand*>(theCmd));
-
-#define NS_REGISTER_NEXT_COMMAND(_cmdClass, _cmdName) \
-  aCommandTable->RegisterCommand(_cmdName,            \
-                                 static_cast<nsIControllerCommand*>(theCmd));
-
-#define NS_REGISTER_LAST_COMMAND(_cmdClass, _cmdName)                         \
-  aCommandTable->RegisterCommand(_cmdName,                                    \
-                                 static_cast<nsIControllerCommand*>(theCmd)); \
+#define NS_REGISTER_COMMAND(_cmdClass, _cmdName)                       \
+  {                                                                    \
+    aCommandTable->RegisterCommand(                                    \
+        _cmdName,                                                      \
+        static_cast<nsIControllerCommand*>(_cmdClass::GetInstance())); \
   }
 
 // static
 nsresult EditorController::RegisterEditingCommands(
     nsControllerCommandTable* aCommandTable) {
   // now register all our commands
   // These are commands that will be used in text widgets, and in composer
 
-  NS_REGISTER_ONE_COMMAND(UndoCommand, "cmd_undo");
-  NS_REGISTER_ONE_COMMAND(RedoCommand, "cmd_redo");
+  NS_REGISTER_COMMAND(UndoCommand, "cmd_undo");
+  NS_REGISTER_COMMAND(RedoCommand, "cmd_redo");
 
-  NS_REGISTER_ONE_COMMAND(CutCommand, "cmd_cut");
-  NS_REGISTER_ONE_COMMAND(CutOrDeleteCommand, "cmd_cutOrDelete");
-  NS_REGISTER_ONE_COMMAND(CopyCommand, "cmd_copy");
-  NS_REGISTER_ONE_COMMAND(CopyOrDeleteCommand, "cmd_copyOrDelete");
-  NS_REGISTER_ONE_COMMAND(SelectAllCommand, "cmd_selectAll");
+  NS_REGISTER_COMMAND(CutCommand, "cmd_cut");
+  NS_REGISTER_COMMAND(CutOrDeleteCommand, "cmd_cutOrDelete");
+  NS_REGISTER_COMMAND(CopyCommand, "cmd_copy");
+  NS_REGISTER_COMMAND(CopyOrDeleteCommand, "cmd_copyOrDelete");
+  NS_REGISTER_COMMAND(SelectAllCommand, "cmd_selectAll");
 
-  NS_REGISTER_ONE_COMMAND(PasteCommand, "cmd_paste");
-  NS_REGISTER_ONE_COMMAND(PasteTransferableCommand, "cmd_pasteTransferable");
+  NS_REGISTER_COMMAND(PasteCommand, "cmd_paste");
+  NS_REGISTER_COMMAND(PasteTransferableCommand, "cmd_pasteTransferable");
 
-  NS_REGISTER_ONE_COMMAND(SwitchTextDirectionCommand,
-                          "cmd_switchTextDirection");
+  NS_REGISTER_COMMAND(SwitchTextDirectionCommand, "cmd_switchTextDirection");
 
-  NS_REGISTER_FIRST_COMMAND(DeleteCommand, "cmd_delete");
-  NS_REGISTER_NEXT_COMMAND(DeleteCommand, "cmd_deleteCharBackward");
-  NS_REGISTER_NEXT_COMMAND(DeleteCommand, "cmd_deleteCharForward");
-  NS_REGISTER_NEXT_COMMAND(DeleteCommand, "cmd_deleteWordBackward");
-  NS_REGISTER_NEXT_COMMAND(DeleteCommand, "cmd_deleteWordForward");
-  NS_REGISTER_NEXT_COMMAND(DeleteCommand, "cmd_deleteToBeginningOfLine");
-  NS_REGISTER_LAST_COMMAND(DeleteCommand, "cmd_deleteToEndOfLine");
+  NS_REGISTER_COMMAND(DeleteCommand, "cmd_delete");
+  NS_REGISTER_COMMAND(DeleteCommand, "cmd_deleteCharBackward");
+  NS_REGISTER_COMMAND(DeleteCommand, "cmd_deleteCharForward");
+  NS_REGISTER_COMMAND(DeleteCommand, "cmd_deleteWordBackward");
+  NS_REGISTER_COMMAND(DeleteCommand, "cmd_deleteWordForward");
+  NS_REGISTER_COMMAND(DeleteCommand, "cmd_deleteToBeginningOfLine");
+  NS_REGISTER_COMMAND(DeleteCommand, "cmd_deleteToEndOfLine");
 
   // Insert content
-  NS_REGISTER_ONE_COMMAND(InsertPlaintextCommand, "cmd_insertText");
-  NS_REGISTER_ONE_COMMAND(InsertParagraphCommand, "cmd_insertParagraph");
-  NS_REGISTER_ONE_COMMAND(InsertLineBreakCommand, "cmd_insertLineBreak");
-  NS_REGISTER_ONE_COMMAND(PasteQuotationCommand, "cmd_pasteQuote");
+  NS_REGISTER_COMMAND(InsertPlaintextCommand, "cmd_insertText");
+  NS_REGISTER_COMMAND(InsertParagraphCommand, "cmd_insertParagraph");
+  NS_REGISTER_COMMAND(InsertLineBreakCommand, "cmd_insertLineBreak");
+  NS_REGISTER_COMMAND(PasteQuotationCommand, "cmd_pasteQuote");
 
   return NS_OK;
 }
 
 // static
 nsresult EditorController::RegisterEditorCommands(
     nsControllerCommandTable* aCommandTable) {
   // These are commands that will be used in text widgets only.
 
-  NS_REGISTER_FIRST_COMMAND(SelectionMoveCommands, "cmd_scrollTop");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_scrollBottom");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_moveTop");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_moveBottom");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectTop");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectBottom");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_lineNext");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_linePrevious");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectLineNext");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectLinePrevious");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_charPrevious");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_charNext");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectCharPrevious");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectCharNext");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_beginLine");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_endLine");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectBeginLine");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectEndLine");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_wordPrevious");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_wordNext");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectWordPrevious");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectWordNext");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_scrollPageUp");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_scrollPageDown");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_scrollLineUp");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_scrollLineDown");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_movePageUp");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_movePageDown");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectPageUp");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectPageDown");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_moveLeft");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_moveRight");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_moveUp");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_moveDown");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_moveLeft2");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_moveRight2");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_moveUp2");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_moveDown2");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectLeft");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectRight");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectUp");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectDown");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectLeft2");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectRight2");
-  NS_REGISTER_NEXT_COMMAND(SelectionMoveCommands, "cmd_selectUp2");
-  NS_REGISTER_LAST_COMMAND(SelectionMoveCommands, "cmd_selectDown2");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_scrollTop");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_scrollBottom");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_moveTop");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_moveBottom");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectTop");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectBottom");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_lineNext");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_linePrevious");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectLineNext");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectLinePrevious");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_charPrevious");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_charNext");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectCharPrevious");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectCharNext");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_beginLine");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_endLine");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectBeginLine");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectEndLine");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_wordPrevious");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_wordNext");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectWordPrevious");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectWordNext");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_scrollPageUp");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_scrollPageDown");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_scrollLineUp");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_scrollLineDown");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_movePageUp");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_movePageDown");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectPageUp");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectPageDown");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_moveLeft");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_moveRight");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_moveUp");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_moveDown");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_moveLeft2");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_moveRight2");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_moveUp2");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_moveDown2");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectLeft");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectRight");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectUp");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectDown");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectLeft2");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectRight2");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectUp2");
+  NS_REGISTER_COMMAND(SelectionMoveCommands, "cmd_selectDown2");
 
   return NS_OK;
 }
 
+// static
+void EditorController::Shutdown() {
+  // EditingCommands
+  UndoCommand::Shutdown();
+  RedoCommand::Shutdown();
+  CutCommand::Shutdown();
+  CutOrDeleteCommand::Shutdown();
+  CopyCommand::Shutdown();
+  CopyOrDeleteCommand::Shutdown();
+  PasteCommand::Shutdown();
+  PasteTransferableCommand::Shutdown();
+  SwitchTextDirectionCommand::Shutdown();
+  DeleteCommand::Shutdown();
+  SelectAllCommand::Shutdown();
+  InsertPlaintextCommand::Shutdown();
+  InsertParagraphCommand::Shutdown();
+  InsertLineBreakCommand::Shutdown();
+  PasteQuotationCommand::Shutdown();
+
+  // EditorCommands
+  SelectionMoveCommands::Shutdown();
+}
+
 }  // namespace mozilla
--- a/editor/libeditor/EditorController.h
+++ b/editor/libeditor/EditorController.h
@@ -17,13 +17,14 @@ namespace mozilla {
 // nsIEditor.
 
 class EditorController final {
  public:
   static nsresult RegisterEditorCommands(
       nsControllerCommandTable* aCommandTable);
   static nsresult RegisterEditingCommands(
       nsControllerCommandTable* aCommandTable);
+  static void Shutdown();
 };
 
 }  // namespace mozilla
 
 #endif  // #ifndef mozilla_EditorController_h
--- a/editor/libeditor/HTMLEditorCommands.cpp
+++ b/editor/libeditor/HTMLEditorCommands.cpp
@@ -43,26 +43,28 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Need
 #define STATE_ALL "state_all"
 #define STATE_ANY "state_any"
 #define STATE_MIXED "state_mixed"
 #define STATE_BEGIN "state_begin"
 #define STATE_END "state_end"
 #define STATE_ATTRIBUTE "state_attribute"
 #define STATE_DATA "state_data"
 
-HTMLEditorCommandBase::HTMLEditorCommandBase() {}
+/*****************************************************************************
+ * mozilla::HTMLEditorCommandBase
+ *****************************************************************************/
 
 NS_IMPL_ISUPPORTS(HTMLEditorCommandBase, nsIControllerCommand)
 
-StateUpdatingCommandBase::StateUpdatingCommandBase(nsAtom* aTagName)
-    : HTMLEditorCommandBase(), mTagName(aTagName) {
-  MOZ_ASSERT(mTagName);
-}
+/*****************************************************************************
+ * mozilla::StateUpdatingCommandBase
+ *****************************************************************************/
 
-StateUpdatingCommandBase::~StateUpdatingCommandBase() {}
+nsRefPtrHashtable<nsCharPtrHashKey, nsAtom>
+    StateUpdatingCommandBase::sTagNameTable;
 
 NS_IMETHODIMP
 StateUpdatingCommandBase::IsCommandEnabled(const char* aCommandName,
                                            nsISupports* refCon,
                                            bool* outCmdEnabled) {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     *outCmdEnabled = false;
@@ -80,17 +82,21 @@ StateUpdatingCommandBase::DoCommand(cons
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (NS_WARN_IF(!htmlEditor)) {
     return NS_ERROR_FAILURE;
   }
-  return ToggleState(MOZ_KnownLive(htmlEditor));
+  RefPtr<nsAtom> tagName = TagName(aCommandName);
+  if (NS_WARN_IF(!tagName)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+  return ToggleState(tagName, MOZ_KnownLive(htmlEditor));
 }
 
 NS_IMETHODIMP
 StateUpdatingCommandBase::DoCommandParams(const char* aCommandName,
                                           nsICommandParams* aParams,
                                           nsISupports* refCon) {
   return DoCommand(aCommandName, refCon);
 }
@@ -102,19 +108,29 @@ StateUpdatingCommandBase::GetCommandStat
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     return NS_OK;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (NS_WARN_IF(!htmlEditor)) {
     return NS_ERROR_FAILURE;
   }
-  return GetCurrentState(htmlEditor, aParams);
+  RefPtr<nsAtom> tagName = TagName(aCommandName);
+  if (NS_WARN_IF(!tagName)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+  return GetCurrentState(tagName, htmlEditor, aParams);
 }
 
+/*****************************************************************************
+ * mozilla::PasteNoFormattingCommand
+ *****************************************************************************/
+
+StaticRefPtr<PasteNoFormattingCommand> PasteNoFormattingCommand::sInstance;
+
 NS_IMETHODIMP
 PasteNoFormattingCommand::IsCommandEnabled(const char* aCommandName,
                                            nsISupports* refCon,
                                            bool* outCmdEnabled) {
   NS_ENSURE_ARG_POINTER(outCmdEnabled);
   *outCmdEnabled = false;
 
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
@@ -163,199 +179,211 @@ PasteNoFormattingCommand::GetCommandStat
 
   bool enabled = false;
   nsresult rv = IsCommandEnabled(aCommandName, refCon, &enabled);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, enabled);
 }
 
-StyleUpdatingCommand::StyleUpdatingCommand(nsAtom* aTagName)
-    : StateUpdatingCommandBase(aTagName) {}
+/*****************************************************************************
+ * mozilla::StyleUpdatingCommand
+ *****************************************************************************/
 
-nsresult StyleUpdatingCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
+StaticRefPtr<StyleUpdatingCommand> StyleUpdatingCommand::sInstance;
+
+nsresult StyleUpdatingCommand::GetCurrentState(nsAtom* aTagName,
+                                               HTMLEditor* aHTMLEditor,
                                                nsICommandParams* aParams) {
-  if (NS_WARN_IF(!aHTMLEditor)) {
+  if (NS_WARN_IF(!aTagName) || NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   bool firstOfSelectionHasProp = false;
   bool anyOfSelectionHasProp = false;
   bool allOfSelectionHasProp = false;
 
   nsresult rv = aHTMLEditor->GetInlineProperty(
-      mTagName, nullptr, EmptyString(), &firstOfSelectionHasProp,
+      aTagName, nullptr, EmptyString(), &firstOfSelectionHasProp,
       &anyOfSelectionHasProp, &allOfSelectionHasProp);
 
   nsCommandParams* params = aParams->AsCommandParams();
   params->SetBool(STATE_ENABLED, NS_SUCCEEDED(rv));
   params->SetBool(STATE_ALL, allOfSelectionHasProp);
   params->SetBool(STATE_ANY, anyOfSelectionHasProp);
   params->SetBool(STATE_MIXED, anyOfSelectionHasProp && !allOfSelectionHasProp);
   params->SetBool(STATE_BEGIN, firstOfSelectionHasProp);
   params->SetBool(STATE_END, allOfSelectionHasProp);  // not completely accurate
   return NS_OK;
 }
 
-nsresult StyleUpdatingCommand::ToggleState(HTMLEditor* aHTMLEditor) {
-  if (NS_WARN_IF(!aHTMLEditor)) {
+nsresult StyleUpdatingCommand::ToggleState(nsAtom* aTagName,
+                                           HTMLEditor* aHTMLEditor) {
+  if (NS_WARN_IF(!aTagName) || NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   RefPtr<nsCommandParams> params = new nsCommandParams();
 
   // tags "href" and "name" are special cases in the core editor
   // they are used to remove named anchor/link and shouldn't be used for
   // insertion
   bool doTagRemoval;
-  if (mTagName == nsGkAtoms::href || mTagName == nsGkAtoms::name) {
+  if (aTagName == nsGkAtoms::href || aTagName == nsGkAtoms::name) {
     doTagRemoval = true;
   } else {
     // check current selection; set doTagRemoval if formatting should be removed
-    nsresult rv = GetCurrentState(aHTMLEditor, params);
+    nsresult rv = GetCurrentState(aTagName, aHTMLEditor, params);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     ErrorResult error;
     doTagRemoval = params->GetBool(STATE_ALL, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
   }
 
   if (doTagRemoval) {
     // Also remove equivalent properties (bug 317093)
     // XXX Why don't we make the following two transactions as an atomic
     //     transaction?  If the element is <b>, <i> or <strike>, user
     //     needs to undo twice.
-    if (mTagName == nsGkAtoms::b) {
+    if (aTagName == nsGkAtoms::b) {
       nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
           *nsGkAtoms::strong, nullptr);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
-    } else if (mTagName == nsGkAtoms::i) {
+    } else if (aTagName == nsGkAtoms::i) {
       nsresult rv =
           aHTMLEditor->RemoveInlinePropertyAsAction(*nsGkAtoms::em, nullptr);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
-    } else if (mTagName == nsGkAtoms::strike) {
+    } else if (aTagName == nsGkAtoms::strike) {
       nsresult rv =
           aHTMLEditor->RemoveInlinePropertyAsAction(*nsGkAtoms::s, nullptr);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
-    nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
-        MOZ_KnownLive(*mTagName), nullptr);
+    nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(*aTagName, nullptr);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(MOZ_KnownLive(*mTagName),
-                                                       nullptr, EmptyString());
+  nsresult rv =
+      aHTMLEditor->SetInlinePropertyAsAction(*aTagName, nullptr, EmptyString());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
-ListCommand::ListCommand(nsAtom* aTagName)
-    : StateUpdatingCommandBase(aTagName) {}
+/*****************************************************************************
+ * mozilla::ListCommand
+ *****************************************************************************/
 
-nsresult ListCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
+StaticRefPtr<ListCommand> ListCommand::sInstance;
+
+nsresult ListCommand::GetCurrentState(nsAtom* aTagName, HTMLEditor* aHTMLEditor,
                                       nsICommandParams* aParams) {
-  if (NS_WARN_IF(!aHTMLEditor)) {
+  if (NS_WARN_IF(!aTagName) || NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   bool bMixed;
   nsAutoString localName;
   nsresult rv = GetListState(aHTMLEditor, &bMixed, localName);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  bool inList = mTagName->Equals(localName);
+  bool inList = aTagName->Equals(localName);
   nsCommandParams* params = aParams->AsCommandParams();
   params->SetBool(STATE_ALL, !bMixed && inList);
   params->SetBool(STATE_MIXED, bMixed);
   params->SetBool(STATE_ENABLED, true);
   return NS_OK;
 }
 
-nsresult ListCommand::ToggleState(HTMLEditor* aHTMLEditor) {
-  if (NS_WARN_IF(!aHTMLEditor)) {
+nsresult ListCommand::ToggleState(nsAtom* aTagName, HTMLEditor* aHTMLEditor) {
+  if (NS_WARN_IF(!aTagName) || NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsresult rv;
   RefPtr<nsCommandParams> params = new nsCommandParams();
-  rv = GetCurrentState(aHTMLEditor, params);
+  rv = GetCurrentState(aTagName, aHTMLEditor, params);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   ErrorResult error;
   bool inList = params->GetBool(STATE_ALL, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
-  nsDependentAtomString listType(mTagName);
+  nsDependentAtomString listType(aTagName);
   if (inList) {
     rv = aHTMLEditor->RemoveList(listType);
   } else {
     rv = aHTMLEditor->MakeOrChangeList(listType, false, EmptyString());
   }
 
   return rv;
 }
 
-ListItemCommand::ListItemCommand(nsAtom* aTagName)
-    : StateUpdatingCommandBase(aTagName) {}
+/*****************************************************************************
+ * mozilla::ListItemCommand
+ *****************************************************************************/
 
-nsresult ListItemCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
+StaticRefPtr<ListItemCommand> ListItemCommand::sInstance;
+
+nsresult ListItemCommand::GetCurrentState(nsAtom* aTagName,
+                                          HTMLEditor* aHTMLEditor,
                                           nsICommandParams* aParams) {
-  if (NS_WARN_IF(!aHTMLEditor)) {
+  if (NS_WARN_IF(!aTagName) || NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   bool bMixed, bLI, bDT, bDD;
   nsresult rv = aHTMLEditor->GetListItemState(&bMixed, &bLI, &bDT, &bDD);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool inList = false;
   if (!bMixed) {
     if (bLI) {
-      inList = mTagName == nsGkAtoms::li;
+      inList = aTagName == nsGkAtoms::li;
     } else if (bDT) {
-      inList = mTagName == nsGkAtoms::dt;
+      inList = aTagName == nsGkAtoms::dt;
     } else if (bDD) {
-      inList = mTagName == nsGkAtoms::dd;
+      inList = aTagName == nsGkAtoms::dd;
     }
   }
 
   nsCommandParams* params = aParams->AsCommandParams();
   params->SetBool(STATE_ALL, !bMixed && inList);
   params->SetBool(STATE_MIXED, bMixed);
 
   return NS_OK;
 }
 
-nsresult ListItemCommand::ToggleState(HTMLEditor* aHTMLEditor) {
-  if (NS_WARN_IF(!aHTMLEditor)) {
+nsresult ListItemCommand::ToggleState(nsAtom* aTagName,
+                                      HTMLEditor* aHTMLEditor) {
+  if (NS_WARN_IF(!aTagName) || NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  // Need to use mTagName????
+  // Need to use aTagName????
   RefPtr<nsCommandParams> params = new nsCommandParams();
-  GetCurrentState(aHTMLEditor, params);
+  GetCurrentState(aTagName, aHTMLEditor, params);
   ErrorResult error;
   bool inList = params->GetBool(STATE_ALL, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
   if (inList) {
     // To remove a list, first get what kind of list we're in
@@ -370,19 +398,25 @@ nsresult ListItemCommand::ToggleState(HT
     }
     return aHTMLEditor->RemoveList(localName);
   }
 
   // Set to the requested paragraph type
   // XXX Note: This actually doesn't work for "LI",
   //    but we currently don't use this for non DL lists anyway.
   // Problem: won't this replace any current block paragraph style?
-  return aHTMLEditor->SetParagraphFormat(nsDependentAtomString(mTagName));
+  return aHTMLEditor->SetParagraphFormat(nsDependentAtomString(aTagName));
 }
 
+/*****************************************************************************
+ * mozilla::RemoveListCommand
+ *****************************************************************************/
+
+StaticRefPtr<RemoveListCommand> RemoveListCommand::sInstance;
+
 NS_IMETHODIMP
 RemoveListCommand::IsCommandEnabled(const char* aCommandName,
                                     nsISupports* refCon, bool* outCmdEnabled) {
   *outCmdEnabled = false;
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     return NS_OK;
   }
@@ -434,16 +468,22 @@ NS_IMETHODIMP
 RemoveListCommand::GetCommandStateParams(const char* aCommandName,
                                          nsICommandParams* aParams,
                                          nsISupports* refCon) {
   bool outCmdEnabled = false;
   IsCommandEnabled(aCommandName, refCon, &outCmdEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, outCmdEnabled);
 }
 
+/*****************************************************************************
+ * mozilla::IndentCommand
+ *****************************************************************************/
+
+StaticRefPtr<IndentCommand> IndentCommand::sInstance;
+
 NS_IMETHODIMP
 IndentCommand::IsCommandEnabled(const char* aCommandName, nsISupports* refCon,
                                 bool* outCmdEnabled) {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     *outCmdEnabled = false;
     return NS_OK;
   }
@@ -480,17 +520,21 @@ NS_IMETHODIMP
 IndentCommand::GetCommandStateParams(const char* aCommandName,
                                      nsICommandParams* aParams,
                                      nsISupports* refCon) {
   bool outCmdEnabled = false;
   IsCommandEnabled(aCommandName, refCon, &outCmdEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, outCmdEnabled);
 }
 
-// OUTDENT
+/*****************************************************************************
+ * mozilla::OutdentCommand
+ *****************************************************************************/
+
+StaticRefPtr<OutdentCommand> OutdentCommand::sInstance;
 
 NS_IMETHODIMP
 OutdentCommand::IsCommandEnabled(const char* aCommandName, nsISupports* refCon,
                                  bool* outCmdEnabled) {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     *outCmdEnabled = false;
     return NS_OK;
@@ -529,19 +573,19 @@ NS_IMETHODIMP
 OutdentCommand::GetCommandStateParams(const char* aCommandName,
                                       nsICommandParams* aParams,
                                       nsISupports* refCon) {
   bool outCmdEnabled = false;
   IsCommandEnabled(aCommandName, refCon, &outCmdEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, outCmdEnabled);
 }
 
-MultiStateCommandBase::MultiStateCommandBase() : HTMLEditorCommandBase() {}
-
-MultiStateCommandBase::~MultiStateCommandBase() {}
+/*****************************************************************************
+ * mozilla::MultiStateCommandBase
+ *****************************************************************************/
 
 NS_IMETHODIMP
 MultiStateCommandBase::IsCommandEnabled(const char* aCommandName,
                                         nsISupports* refCon,
                                         bool* outCmdEnabled) {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     *outCmdEnabled = false;
@@ -604,17 +648,21 @@ MultiStateCommandBase::GetCommandStatePa
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (NS_WARN_IF(!htmlEditor)) {
     return NS_ERROR_FAILURE;
   }
   return GetCurrentState(MOZ_KnownLive(htmlEditor), aParams);
 }
 
-ParagraphStateCommand::ParagraphStateCommand() : MultiStateCommandBase() {}
+/*****************************************************************************
+ * mozilla::ParagraphStateCommand
+ *****************************************************************************/
+
+StaticRefPtr<ParagraphStateCommand> ParagraphStateCommand::sInstance;
 
 nsresult ParagraphStateCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
                                                 nsICommandParams* aParams) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   bool outMixed;
@@ -633,17 +681,21 @@ nsresult ParagraphStateCommand::GetCurre
 nsresult ParagraphStateCommand::SetState(HTMLEditor* aHTMLEditor,
                                          const nsString& newState) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
   return aHTMLEditor->SetParagraphFormat(newState);
 }
 
-FontFaceStateCommand::FontFaceStateCommand() : MultiStateCommandBase() {}
+/*****************************************************************************
+ * mozilla::FontFaceStateCommand
+ *****************************************************************************/
+
+StaticRefPtr<FontFaceStateCommand> FontFaceStateCommand::sInstance;
 
 nsresult FontFaceStateCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
                                                nsICommandParams* aParams) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsAutoString outStateString;
@@ -698,17 +750,21 @@ nsresult FontFaceStateCommand::SetState(
   rv = aHTMLEditor->SetInlinePropertyAsAction(*nsGkAtoms::font, nsGkAtoms::face,
                                               newState);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
-FontSizeStateCommand::FontSizeStateCommand() : MultiStateCommandBase() {}
+/*****************************************************************************
+ * mozilla::FontSizeStateCommand
+ *****************************************************************************/
+
+StaticRefPtr<FontSizeStateCommand> FontSizeStateCommand::sInstance;
 
 nsresult FontSizeStateCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
                                                nsICommandParams* aParams) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsAutoString outStateString;
@@ -767,17 +823,21 @@ nsresult FontSizeStateCommand::SetState(
 
   rv = aHTMLEditor->RemoveInlinePropertyAsAction(*nsGkAtoms::small, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
-FontColorStateCommand::FontColorStateCommand() : MultiStateCommandBase() {}
+/*****************************************************************************
+ * mozilla::FontColorStateCommand
+ *****************************************************************************/
+
+StaticRefPtr<FontColorStateCommand> FontColorStateCommand::sInstance;
 
 nsresult FontColorStateCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
                                                 nsICommandParams* aParams) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   bool outMixed;
@@ -813,18 +873,21 @@ nsresult FontColorStateCommand::SetState
   nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(
       *nsGkAtoms::font, nsGkAtoms::color, newState);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
-HighlightColorStateCommand::HighlightColorStateCommand()
-    : MultiStateCommandBase() {}
+/*****************************************************************************
+ * mozilla::HighlightColorStateCommand
+ *****************************************************************************/
+
+StaticRefPtr<HighlightColorStateCommand> HighlightColorStateCommand::sInstance;
 
 nsresult HighlightColorStateCommand::GetCurrentState(
     HTMLEditor* aHTMLEditor, nsICommandParams* aParams) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   bool outMixed;
@@ -873,18 +936,22 @@ HighlightColorStateCommand::IsCommandEna
     return NS_OK;
   }
   mozilla::EditorBase* editorBase = editor->AsEditorBase();
   MOZ_ASSERT(editorBase);
   *outCmdEnabled = editorBase->IsSelectionEditable();
   return NS_OK;
 }
 
-BackgroundColorStateCommand::BackgroundColorStateCommand()
-    : MultiStateCommandBase() {}
+/*****************************************************************************
+ * mozilla::BackgroundColorStateCommand
+ *****************************************************************************/
+
+StaticRefPtr<BackgroundColorStateCommand>
+    BackgroundColorStateCommand::sInstance;
 
 nsresult BackgroundColorStateCommand::GetCurrentState(
     HTMLEditor* aHTMLEditor, nsICommandParams* aParams) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   bool outMixed;
@@ -903,17 +970,21 @@ nsresult BackgroundColorStateCommand::Ge
 nsresult BackgroundColorStateCommand::SetState(HTMLEditor* aHTMLEditor,
                                                const nsString& newState) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
   return aHTMLEditor->SetBackgroundColor(newState);
 }
 
-AlignCommand::AlignCommand() : MultiStateCommandBase() {}
+/*****************************************************************************
+ * mozilla::AlignCommand
+ *****************************************************************************/
+
+StaticRefPtr<AlignCommand> AlignCommand::sInstance;
 
 nsresult AlignCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
                                        nsICommandParams* aParams) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsIHTMLEditor::EAlignment firstAlign;
@@ -952,18 +1023,21 @@ nsresult AlignCommand::GetCurrentState(H
 nsresult AlignCommand::SetState(HTMLEditor* aHTMLEditor,
                                 const nsString& newState) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
   return aHTMLEditor->Align(newState);
 }
 
-AbsolutePositioningCommand::AbsolutePositioningCommand()
-    : StateUpdatingCommandBase(nsGkAtoms::_empty) {}
+/*****************************************************************************
+ * mozilla::AbsolutePositioningCommand
+ *****************************************************************************/
+
+StaticRefPtr<AbsolutePositioningCommand> AbsolutePositioningCommand::sInstance;
 
 NS_IMETHODIMP
 AbsolutePositioningCommand::IsCommandEnabled(const char* aCommandName,
                                              nsISupports* aCommandRefCon,
                                              bool* aOutCmdEnabled) {
   *aOutCmdEnabled = false;
 
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
@@ -977,17 +1051,17 @@ AbsolutePositioningCommand::IsCommandEna
   if (!htmlEditor->IsSelectionEditable()) {
     return NS_OK;
   }
   *aOutCmdEnabled = htmlEditor->IsAbsolutePositionEditorEnabled();
   return NS_OK;
 }
 
 nsresult AbsolutePositioningCommand::GetCurrentState(
-    HTMLEditor* aHTMLEditor, nsICommandParams* aParams) {
+    nsAtom* aTagName, HTMLEditor* aHTMLEditor, nsICommandParams* aParams) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsCommandParams* params = aParams->AsCommandParams();
   if (!aHTMLEditor->IsAbsolutePositionEditorEnabled()) {
     params->SetBool(STATE_MIXED, false);
     params->SetCString(STATE_ATTRIBUTE, EmptyCString());
@@ -997,26 +1071,33 @@ nsresult AbsolutePositioningCommand::Get
   RefPtr<Element> container =
       aHTMLEditor->GetAbsolutelyPositionedSelectionContainer();
   params->SetBool(STATE_MIXED, false);
   params->SetCString(STATE_ATTRIBUTE, container ? NS_LITERAL_CSTRING("absolute")
                                                 : EmptyCString());
   return NS_OK;
 }
 
-nsresult AbsolutePositioningCommand::ToggleState(HTMLEditor* aHTMLEditor) {
+nsresult AbsolutePositioningCommand::ToggleState(nsAtom* aTagName,
+                                                 HTMLEditor* aHTMLEditor) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   RefPtr<Element> container =
       aHTMLEditor->GetAbsolutelyPositionedSelectionContainer();
   return aHTMLEditor->SetSelectionToAbsoluteOrStatic(!container);
 }
 
+/*****************************************************************************
+ * mozilla::DecreaseZIndexCommand
+ *****************************************************************************/
+
+StaticRefPtr<DecreaseZIndexCommand> DecreaseZIndexCommand::sInstance;
+
 NS_IMETHODIMP
 DecreaseZIndexCommand::IsCommandEnabled(const char* aCommandName,
                                         nsISupports* aRefCon,
                                         bool* aOutCmdEnabled) {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aRefCon);
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
@@ -1070,16 +1151,22 @@ DecreaseZIndexCommand::GetCommandStatePa
 
   bool enabled = false;
   nsresult rv = IsCommandEnabled(aCommandName, refCon, &enabled);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, enabled);
 }
 
+/*****************************************************************************
+ * mozilla::IncreaseZIndexCommand
+ *****************************************************************************/
+
+StaticRefPtr<IncreaseZIndexCommand> IncreaseZIndexCommand::sInstance;
+
 NS_IMETHODIMP
 IncreaseZIndexCommand::IsCommandEnabled(const char* aCommandName,
                                         nsISupports* aRefCon,
                                         bool* aOutCmdEnabled) {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aRefCon);
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
@@ -1126,16 +1213,22 @@ IncreaseZIndexCommand::GetCommandStatePa
 
   bool enabled = false;
   nsresult rv = IsCommandEnabled(aCommandName, refCon, &enabled);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, enabled);
 }
 
+/*****************************************************************************
+ * mozilla::RemoveStylesCommand
+ *****************************************************************************/
+
+StaticRefPtr<RemoveStylesCommand> RemoveStylesCommand::sInstance;
+
 NS_IMETHODIMP
 RemoveStylesCommand::IsCommandEnabled(const char* aCommandName,
                                       nsISupports* refCon,
                                       bool* outCmdEnabled) {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     *outCmdEnabled = false;
     return NS_OK;
@@ -1171,16 +1264,22 @@ NS_IMETHODIMP
 RemoveStylesCommand::GetCommandStateParams(const char* aCommandName,
                                            nsICommandParams* aParams,
                                            nsISupports* refCon) {
   bool outCmdEnabled = false;
   IsCommandEnabled(aCommandName, refCon, &outCmdEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, outCmdEnabled);
 }
 
+/*****************************************************************************
+ * mozilla::IncreaseFontSizeCommand
+ *****************************************************************************/
+
+StaticRefPtr<IncreaseFontSizeCommand> IncreaseFontSizeCommand::sInstance;
+
 NS_IMETHODIMP
 IncreaseFontSizeCommand::IsCommandEnabled(const char* aCommandName,
                                           nsISupports* refCon,
                                           bool* outCmdEnabled) {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     *outCmdEnabled = false;
     return NS_OK;
@@ -1217,16 +1316,22 @@ NS_IMETHODIMP
 IncreaseFontSizeCommand::GetCommandStateParams(const char* aCommandName,
                                                nsICommandParams* aParams,
                                                nsISupports* refCon) {
   bool outCmdEnabled = false;
   IsCommandEnabled(aCommandName, refCon, &outCmdEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, outCmdEnabled);
 }
 
+/*****************************************************************************
+ * mozilla::DecreaseFontSizeCommand
+ *****************************************************************************/
+
+StaticRefPtr<DecreaseFontSizeCommand> DecreaseFontSizeCommand::sInstance;
+
 NS_IMETHODIMP
 DecreaseFontSizeCommand::IsCommandEnabled(const char* aCommandName,
                                           nsISupports* refCon,
                                           bool* outCmdEnabled) {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     *outCmdEnabled = false;
     return NS_OK;
@@ -1263,16 +1368,22 @@ NS_IMETHODIMP
 DecreaseFontSizeCommand::GetCommandStateParams(const char* aCommandName,
                                                nsICommandParams* aParams,
                                                nsISupports* refCon) {
   bool outCmdEnabled = false;
   IsCommandEnabled(aCommandName, refCon, &outCmdEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, outCmdEnabled);
 }
 
+/*****************************************************************************
+ * mozilla::InsertHTMLCommand
+ *****************************************************************************/
+
+StaticRefPtr<InsertHTMLCommand> InsertHTMLCommand::sInstance;
+
 NS_IMETHODIMP
 InsertHTMLCommand::IsCommandEnabled(const char* aCommandName,
                                     nsISupports* refCon, bool* outCmdEnabled) {
   NS_ENSURE_ARG_POINTER(outCmdEnabled);
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     *outCmdEnabled = false;
     return NS_OK;
@@ -1334,22 +1445,22 @@ InsertHTMLCommand::GetCommandStateParams
   NS_ENSURE_ARG_POINTER(aParams);
   NS_ENSURE_ARG_POINTER(refCon);
 
   bool outCmdEnabled = false;
   IsCommandEnabled(aCommandName, refCon, &outCmdEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, outCmdEnabled);
 }
 
-InsertTagCommand::InsertTagCommand(nsAtom* aTagName)
-    : HTMLEditorCommandBase(), mTagName(aTagName) {
-  MOZ_ASSERT(mTagName);
-}
+/*****************************************************************************
+ * mozilla::InsertTagCommand
+ *****************************************************************************/
 
-InsertTagCommand::~InsertTagCommand() {}
+StaticRefPtr<InsertTagCommand> InsertTagCommand::sInstance;
+nsRefPtrHashtable<nsCharPtrHashKey, nsAtom> InsertTagCommand::sTagNameTable;
 
 NS_IMETHODIMP
 InsertTagCommand::IsCommandEnabled(const char* aCommandName,
                                    nsISupports* refCon, bool* outCmdEnabled) {
   NS_ENSURE_ARG_POINTER(outCmdEnabled);
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     *outCmdEnabled = false;
@@ -1359,53 +1470,66 @@ InsertTagCommand::IsCommandEnabled(const
   MOZ_ASSERT(editorBase);
   *outCmdEnabled = editorBase->IsSelectionEditable();
   return NS_OK;
 }
 
 // corresponding STATE_ATTRIBUTE is: src (img) and href (a)
 NS_IMETHODIMP
 InsertTagCommand::DoCommand(const char* aCmdName, nsISupports* refCon) {
-  NS_ENSURE_TRUE(mTagName == nsGkAtoms::hr, NS_ERROR_NOT_IMPLEMENTED);
+  if (NS_WARN_IF(!aCmdName) || NS_WARN_IF(!refCon)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  RefPtr<nsAtom> tagName = TagName(aCmdName);
+  if (NS_WARN_IF(tagName != nsGkAtoms::hr)) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
 
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (NS_WARN_IF(!htmlEditor)) {
     return NS_ERROR_FAILURE;
   }
 
   RefPtr<Element> newElement =
-      MOZ_KnownLive(htmlEditor)
-          ->CreateElementWithDefaults(MOZ_KnownLive(*mTagName));
+      MOZ_KnownLive(htmlEditor)->CreateElementWithDefaults(*tagName);
   if (NS_WARN_IF(!newElement)) {
     return NS_ERROR_FAILURE;
   }
   nsresult rv =
       MOZ_KnownLive(htmlEditor)->InsertElementAtSelection(newElement, true);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InsertTagCommand::DoCommandParams(const char* aCommandName,
                                   nsICommandParams* aParams,
                                   nsISupports* refCon) {
-  NS_ENSURE_ARG_POINTER(refCon);
+  if (NS_WARN_IF(!aCommandName) || NS_WARN_IF(!refCon)) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
   // inserting an hr shouldn't have an parameters, just call DoCommand for that
-  if (mTagName == nsGkAtoms::hr) {
+  if (!strcmp(aCommandName, "cmd_insertHR")) {
     return DoCommand(aCommandName, refCon);
   }
 
-  NS_ENSURE_ARG_POINTER(aParams);
+  if (NS_WARN_IF(!aParams)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  RefPtr<nsAtom> tagName = TagName(aCommandName);
+  if (NS_WARN_IF(!tagName)) {
+    return NS_ERROR_UNEXPECTED;
+  }
 
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (NS_WARN_IF(!htmlEditor)) {
     return NS_ERROR_FAILURE;
@@ -1420,39 +1544,38 @@ InsertTagCommand::DoCommandParams(const 
     return rv;
   }
   if (NS_WARN_IF(value.IsEmpty())) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // filter out tags we don't know how to insert
   nsAtom* attribute = nullptr;
-  if (mTagName == nsGkAtoms::a) {
+  if (tagName == nsGkAtoms::a) {
     attribute = nsGkAtoms::href;
-  } else if (mTagName == nsGkAtoms::img) {
+  } else if (tagName == nsGkAtoms::img) {
     attribute = nsGkAtoms::src;
   } else {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   RefPtr<Element> newElement =
-      MOZ_KnownLive(htmlEditor)
-          ->CreateElementWithDefaults(MOZ_KnownLive(*mTagName));
+      MOZ_KnownLive(htmlEditor)->CreateElementWithDefaults(*tagName);
   if (NS_WARN_IF(!newElement)) {
     return NS_ERROR_FAILURE;
   }
 
   ErrorResult err;
   newElement->SetAttr(attribute, value, err);
   if (NS_WARN_IF(err.Failed())) {
     return err.StealNSResult();
   }
 
   // do actual insertion
-  if (mTagName == nsGkAtoms::a) {
+  if (tagName == nsGkAtoms::a) {
     rv = MOZ_KnownLive(htmlEditor)->InsertLinkAroundSelection(newElement);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   rv = MOZ_KnownLive(htmlEditor)->InsertElementAtSelection(newElement, true);
@@ -1469,19 +1592,19 @@ InsertTagCommand::GetCommandStateParams(
   NS_ENSURE_ARG_POINTER(aParams);
   NS_ENSURE_ARG_POINTER(refCon);
 
   bool outCmdEnabled = false;
   IsCommandEnabled(aCommandName, refCon, &outCmdEnabled);
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, outCmdEnabled);
 }
 
-/****************************/
-// HELPER METHODS
-/****************************/
+/*****************************************************************************
+ * Helper methods
+ *****************************************************************************/
 
 static nsresult GetListState(HTMLEditor* aHTMLEditor, bool* aMixed,
                              nsAString& aLocalName)
     MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
   MOZ_ASSERT(aHTMLEditor);
   MOZ_ASSERT(aMixed);
 
   *aMixed = false;
--- a/editor/libeditor/HTMLEditorCommands.h
+++ b/editor/libeditor/HTMLEditorCommands.h
@@ -1,281 +1,393 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_HTMLEditorCommands_h_
 #define mozilla_HTMLEditorCommands_h_
 
+#include "mozilla/StaticPtr.h"
 #include "nsIControllerCommand.h"
 #include "nsISupportsImpl.h"  // for NS_DECL_ISUPPORTS_INHERITED, etc
+#include "nsRefPtrHashtable.h"
 #include "nsStringFwd.h"
 #include "nscore.h"  // for nsresult, NS_IMETHOD
 
 class nsAtom;
 class nsICommandParams;
 class nsISupports;
 
 namespace mozilla {
 class HTMLEditor;
 
-// This is a virtual base class for commands registered with the composer
-// controller. Note that such commands are instantiated once per composer, so
-// can store state. Also note that IsCommandEnabled can be called with an editor
-// that may not have an editor yet (because the document is loading). Most
-// commands will want to return false in this case. Don't hold on to any
-// references to the editor or document from your command. This will cause
-// leaks. Also, be aware that the document the editor is editing can change
-// under you (if the user Reverts the file, for instance).
+// This is a base class for commands registered with window which includes
+// HTMLEditor.  Like editor commands, these command classes are also designed
+// as singleton classes.  So, MUST be stateless.  Also note that
+// IsCommandEnabled can be called with an editor that may not have an editor
+// yet (because the document is loading).  Most commands will want to return
+// false in this case.  Don't hold on to any references to the editor or
+// document from your command.  This will cause leaks.  Also, be aware that
+// the document the editor is editing can change under you (if the user
+// Reverts the file, for instance).
 class HTMLEditorCommandBase : public nsIControllerCommand {
- protected:
-  virtual ~HTMLEditorCommandBase() {}
-
  public:
-  HTMLEditorCommandBase();
-
   // nsISupports
   NS_DECL_ISUPPORTS
+
+ protected:
+  HTMLEditorCommandBase() = default;
+  virtual ~HTMLEditorCommandBase() = default;
 };
 
-#define NS_DECL_COMPOSER_COMMAND(_cmd)              \
+#define NS_DECL_HTML_EDITOR_COMMAND(_cmd)           \
   class _cmd final : public HTMLEditorCommandBase { \
    public:                                          \
     NS_DECL_NSICONTROLLERCOMMAND                    \
+                                                    \
+    static _cmd* GetInstance() {                    \
+      if (!sInstance) {                             \
+        sInstance = new _cmd();                     \
+      }                                             \
+      return sInstance;                             \
+    }                                               \
+                                                    \
+    static void Shutdown() { sInstance = nullptr; } \
+                                                    \
+   private:                                         \
+    static StaticRefPtr<_cmd> sInstance;            \
   };
 
+#define NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(_cmd) \
+ public:                                            \
+  static _cmd* GetInstance() {                      \
+    if (!sInstance) {                               \
+      sInstance = new _cmd();                       \
+    }                                               \
+    return sInstance;                               \
+  }                                                 \
+                                                    \
+  static void Shutdown() { sInstance = nullptr; }   \
+                                                    \
+ private:                                           \
+  static StaticRefPtr<_cmd> sInstance;
+
 // virtual base class for commands that need to save and update Boolean state
 // (like styles etc)
 class StateUpdatingCommandBase : public HTMLEditorCommandBase {
  public:
-  explicit StateUpdatingCommandBase(nsAtom* aTagName);
-
   NS_INLINE_DECL_REFCOUNTING_INHERITED(StateUpdatingCommandBase,
                                        HTMLEditorCommandBase)
 
   NS_DECL_NSICONTROLLERCOMMAND
 
  protected:
-  virtual ~StateUpdatingCommandBase();
+  StateUpdatingCommandBase() = default;
+  virtual ~StateUpdatingCommandBase() { sTagNameTable.Clear(); }
 
   // get the current state (on or off) for this style or block format
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       virtual nsresult
-      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) = 0;
+      GetCurrentState(nsAtom* aTagName, HTMLEditor* aHTMLEditor,
+                      nsICommandParams* aParams) = 0;
 
   // add/remove the style
   MOZ_CAN_RUN_SCRIPT
-  virtual nsresult ToggleState(HTMLEditor* aHTMLEditor) = 0;
+  virtual nsresult ToggleState(nsAtom* aTagName, HTMLEditor* aHTMLEditor) = 0;
 
- protected:
-  nsAtom* mTagName;
+  static already_AddRefed<nsAtom> TagName(const char* aCommandName) {
+    MOZ_DIAGNOSTIC_ASSERT(aCommandName);
+    if (NS_WARN_IF(!aCommandName)) {
+      return nullptr;
+    }
+    if (!sTagNameTable.Count()) {
+      sTagNameTable.Put("cmd_bold", nsGkAtoms::b);
+      sTagNameTable.Put("cmd_italic", nsGkAtoms::i);
+      sTagNameTable.Put("cmd_underline", nsGkAtoms::u);
+      sTagNameTable.Put("cmd_tt", nsGkAtoms::tt);
+      sTagNameTable.Put("cmd_strikethrough", nsGkAtoms::strike);
+      sTagNameTable.Put("cmd_superscript", nsGkAtoms::sup);
+      sTagNameTable.Put("cmd_subscript", nsGkAtoms::sub);
+      sTagNameTable.Put("cmd_nobreak", nsGkAtoms::nobr);
+      sTagNameTable.Put("cmd_em", nsGkAtoms::em);
+      sTagNameTable.Put("cmd_strong", nsGkAtoms::strong);
+      sTagNameTable.Put("cmd_cite", nsGkAtoms::cite);
+      sTagNameTable.Put("cmd_abbr", nsGkAtoms::abbr);
+      sTagNameTable.Put("cmd_acronym", nsGkAtoms::acronym);
+      sTagNameTable.Put("cmd_code", nsGkAtoms::code);
+      sTagNameTable.Put("cmd_samp", nsGkAtoms::samp);
+      sTagNameTable.Put("cmd_var", nsGkAtoms::var);
+      sTagNameTable.Put("cmd_removeLinks", nsGkAtoms::href);
+      sTagNameTable.Put("cmd_ol", nsGkAtoms::ol);
+      sTagNameTable.Put("cmd_ul", nsGkAtoms::ul);
+      sTagNameTable.Put("cmd_dt", nsGkAtoms::dt);
+      sTagNameTable.Put("cmd_dd", nsGkAtoms::dd);
+      sTagNameTable.Put("cmd_absPos", nsGkAtoms::_empty);
+    }
+    RefPtr<nsAtom> tagName = sTagNameTable.Get(aCommandName);
+    MOZ_DIAGNOSTIC_ASSERT(tagName);
+    return tagName.forget();
+  }
+
+  static nsRefPtrHashtable<nsCharPtrHashKey, nsAtom> sTagNameTable;
 };
 
 // Shared class for the various style updating commands like bold, italics etc.
 // Suitable for commands whose state is either 'on' or 'off'.
 class StyleUpdatingCommand final : public StateUpdatingCommandBase {
  public:
-  explicit StyleUpdatingCommand(nsAtom* aTagName);
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(StyleUpdatingCommand)
 
  protected:
+  StyleUpdatingCommand() = default;
+  virtual ~StyleUpdatingCommand() = default;
+
   // get the current state (on or off) for this style or block format
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
-      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+      GetCurrentState(nsAtom* aTagName, HTMLEditor* aHTMLEditor,
+                      nsICommandParams* aParams) final;
 
   // add/remove the style
   MOZ_CAN_RUN_SCRIPT
-  nsresult ToggleState(HTMLEditor* aHTMLEditor) final;
+  nsresult ToggleState(nsAtom* aTagName, HTMLEditor* aHTMLEditor) final;
 };
 
 class InsertTagCommand final : public HTMLEditorCommandBase {
  public:
-  explicit InsertTagCommand(nsAtom* aTagName);
-
   NS_INLINE_DECL_REFCOUNTING_INHERITED(InsertTagCommand, HTMLEditorCommandBase)
 
   NS_DECL_NSICONTROLLERCOMMAND
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(InsertTagCommand)
 
  protected:
-  virtual ~InsertTagCommand();
+  InsertTagCommand() = default;
+  virtual ~InsertTagCommand() { sTagNameTable.Clear(); }
 
-  nsAtom* mTagName;
+  static already_AddRefed<nsAtom> TagName(const char* aCommandName) {
+    MOZ_DIAGNOSTIC_ASSERT(aCommandName);
+    if (NS_WARN_IF(!aCommandName)) {
+      return nullptr;
+    }
+    if (!sTagNameTable.Count()) {
+      sTagNameTable.Put("cmd_insertLinkNoUI", nsGkAtoms::a);
+      sTagNameTable.Put("cmd_insertImageNoUI", nsGkAtoms::img);
+      sTagNameTable.Put("cmd_insertHR", nsGkAtoms::hr);
+    }
+    RefPtr<nsAtom> tagName = sTagNameTable.Get(aCommandName);
+    MOZ_DIAGNOSTIC_ASSERT(tagName);
+    return tagName.forget();
+  }
+
+  static nsRefPtrHashtable<nsCharPtrHashKey, nsAtom> sTagNameTable;
 };
 
 class ListCommand final : public StateUpdatingCommandBase {
  public:
-  explicit ListCommand(nsAtom* aTagName);
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(ListCommand)
 
  protected:
+  ListCommand() = default;
+  virtual ~ListCommand() = default;
+
   // get the current state (on or off) for this style or block format
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
-      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+      GetCurrentState(nsAtom* aTagName, HTMLEditor* aHTMLEditor,
+                      nsICommandParams* aParams) final;
 
   // add/remove the style
   MOZ_CAN_RUN_SCRIPT
-  nsresult ToggleState(HTMLEditor* aHTMLEditor) final;
+  nsresult ToggleState(nsAtom* aTagName, HTMLEditor* aHTMLEditor) final;
 };
 
 class ListItemCommand final : public StateUpdatingCommandBase {
  public:
-  explicit ListItemCommand(nsAtom* aTagName);
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(ListItemCommand)
 
  protected:
+  ListItemCommand() = default;
+  virtual ~ListItemCommand() = default;
+
   // get the current state (on or off) for this style or block format
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
-      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+      GetCurrentState(nsAtom* aTagName, HTMLEditor* aHTMLEditor,
+                      nsICommandParams* aParams) final;
 
   // add/remove the style
   MOZ_CAN_RUN_SCRIPT
-  nsresult ToggleState(HTMLEditor* aHTMLEditor) final;
+  nsresult ToggleState(nsAtom* aTagName, HTMLEditor* aHTMLEditor) final;
 };
 
 // Base class for commands whose state consists of a string (e.g. para format)
 class MultiStateCommandBase : public HTMLEditorCommandBase {
  public:
-  MultiStateCommandBase();
-
   NS_INLINE_DECL_REFCOUNTING_INHERITED(MultiStateCommandBase,
                                        HTMLEditorCommandBase)
   NS_DECL_NSICONTROLLERCOMMAND
 
  protected:
-  virtual ~MultiStateCommandBase();
+  MultiStateCommandBase() = default;
+  virtual ~MultiStateCommandBase() = default;
 
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       virtual nsresult
       GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) = 0;
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult SetState(HTMLEditor* aHTMLEditor,
                             const nsString& newState) = 0;
 };
 
 class ParagraphStateCommand final : public MultiStateCommandBase {
  public:
-  ParagraphStateCommand();
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(ParagraphStateCommand)
 
  protected:
+  ParagraphStateCommand() = default;
+  virtual ~ParagraphStateCommand() = default;
+
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
       GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
   MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class FontFaceStateCommand final : public MultiStateCommandBase {
  public:
-  FontFaceStateCommand();
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(FontFaceStateCommand)
 
  protected:
+  FontFaceStateCommand() = default;
+  virtual ~FontFaceStateCommand() = default;
+
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
       GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
   MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class FontSizeStateCommand final : public MultiStateCommandBase {
  public:
-  FontSizeStateCommand();
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(FontSizeStateCommand)
 
  protected:
+  FontSizeStateCommand() = default;
+  virtual ~FontSizeStateCommand() = default;
+
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
       GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
   MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class HighlightColorStateCommand final : public MultiStateCommandBase {
  public:
-  HighlightColorStateCommand();
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(HighlightColorStateCommand)
 
  protected:
+  HighlightColorStateCommand() = default;
+  virtual ~HighlightColorStateCommand() = default;
+
   NS_IMETHOD IsCommandEnabled(const char* aCommandName,
                               nsISupports* aCommandRefCon, bool* _retval) final;
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
       GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
   MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class FontColorStateCommand final : public MultiStateCommandBase {
  public:
-  FontColorStateCommand();
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(FontColorStateCommand)
 
  protected:
+  FontColorStateCommand() = default;
+  virtual ~FontColorStateCommand() = default;
+
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
       GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
   MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class AlignCommand final : public MultiStateCommandBase {
  public:
-  AlignCommand();
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(AlignCommand)
 
  protected:
+  AlignCommand() = default;
+  virtual ~AlignCommand() = default;
+
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
       GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
   MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class BackgroundColorStateCommand final : public MultiStateCommandBase {
  public:
-  BackgroundColorStateCommand();
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(BackgroundColorStateCommand)
 
  protected:
+  BackgroundColorStateCommand() = default;
+  virtual ~BackgroundColorStateCommand() = default;
+
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
       GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
   MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class AbsolutePositioningCommand final : public StateUpdatingCommandBase {
  public:
-  AbsolutePositioningCommand();
+  NS_DECL_HTML_EDITOR_COMMAND_SINGLETON(AbsolutePositioningCommand)
 
  protected:
+  AbsolutePositioningCommand() = default;
+  virtual ~AbsolutePositioningCommand() = default;
+
   NS_IMETHOD IsCommandEnabled(const char* aCommandName,
                               nsISupports* aCommandRefCon, bool* _retval) final;
   MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
       nsresult
-      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+      GetCurrentState(nsAtom* aTagName, HTMLEditor* aHTMLEditor,
+                      nsICommandParams* aParams) final;
   MOZ_CAN_RUN_SCRIPT
-  nsresult ToggleState(HTMLEditor* aHTMLEditor) final;
+  nsresult ToggleState(nsAtom* aTagName, HTMLEditor* aHTMLEditor) final;
 };
 
 // composer commands
 
-NS_DECL_COMPOSER_COMMAND(DocumentStateCommand)
-NS_DECL_COMPOSER_COMMAND(SetDocumentStateCommand)
+NS_DECL_HTML_EDITOR_COMMAND(DocumentStateCommand)
+NS_DECL_HTML_EDITOR_COMMAND(SetDocumentStateCommand)
 
-NS_DECL_COMPOSER_COMMAND(DecreaseZIndexCommand)
-NS_DECL_COMPOSER_COMMAND(IncreaseZIndexCommand)
+NS_DECL_HTML_EDITOR_COMMAND(DecreaseZIndexCommand)
+NS_DECL_HTML_EDITOR_COMMAND(IncreaseZIndexCommand)
 
 // Generic commands
 
 // Edit menu
-NS_DECL_COMPOSER_COMMAND(PasteNoFormattingCommand)
+NS_DECL_HTML_EDITOR_COMMAND(PasteNoFormattingCommand)
 
 // Block transformations
-NS_DECL_COMPOSER_COMMAND(IndentCommand)
-NS_DECL_COMPOSER_COMMAND(OutdentCommand)
+NS_DECL_HTML_EDITOR_COMMAND(IndentCommand)
+NS_DECL_HTML_EDITOR_COMMAND(OutdentCommand)
 
-NS_DECL_COMPOSER_COMMAND(RemoveListCommand)
-NS_DECL_COMPOSER_COMMAND(RemoveStylesCommand)
-NS_DECL_COMPOSER_COMMAND(IncreaseFontSizeCommand)
-NS_DECL_COMPOSER_COMMAND(DecreaseFontSizeCommand)
+NS_DECL_HTML_EDITOR_COMMAND(RemoveListCommand)
+NS_DECL_HTML_EDITOR_COMMAND(RemoveStylesCommand)
+NS_DECL_HTML_EDITOR_COMMAND(IncreaseFontSizeCommand)
+NS_DECL_HTML_EDITOR_COMMAND(DecreaseFontSizeCommand)
 
 // Insert content commands
-NS_DECL_COMPOSER_COMMAND(InsertHTMLCommand)
+NS_DECL_HTML_EDITOR_COMMAND(InsertHTMLCommand)
 
 }  // namespace mozilla
 
 #endif  // mozilla_HTMLEditorCommands_h_
--- a/editor/libeditor/HTMLEditorController.cpp
+++ b/editor/libeditor/HTMLEditorController.cpp
@@ -4,147 +4,137 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/HTMLEditorController.h"
 
 #include "mozilla/HTMLEditorCommands.h"  // for StyleUpdatingCommand, etc
 #include "mozilla/mozalloc.h"            // for operator new
 #include "nsControllerCommandTable.h"    // for nsControllerCommandTable
 #include "nsError.h"                     // for NS_OK
-#include "nsGkAtoms.h"                   // for nsGkAtoms, nsGkAtoms::a, etc
 
 namespace mozilla {
 
-#define NS_REGISTER_ONE_COMMAND(_cmdClass, _cmdName)            \
-  {                                                             \
-    _cmdClass *theCmd = new _cmdClass();                        \
-    aCommandTable->RegisterCommand(                             \
-        _cmdName, static_cast<nsIControllerCommand *>(theCmd)); \
-  }
-
-#define NS_REGISTER_FIRST_COMMAND(_cmdClass, _cmdName) \
-  {                                                    \
-    _cmdClass *theCmd = new _cmdClass();               \
-    aCommandTable->RegisterCommand(                    \
-        _cmdName, static_cast<nsIControllerCommand *>(theCmd));
-
-#define NS_REGISTER_NEXT_COMMAND(_cmdClass, _cmdName) \
-  aCommandTable->RegisterCommand(_cmdName,            \
-                                 static_cast<nsIControllerCommand *>(theCmd));
-
-#define NS_REGISTER_LAST_COMMAND(_cmdClass, _cmdName)                          \
-  aCommandTable->RegisterCommand(_cmdName,                                     \
-                                 static_cast<nsIControllerCommand *>(theCmd)); \
-  }
-
-#define NS_REGISTER_STYLE_COMMAND(_cmdClass, _cmdName, _styleTag) \
-  {                                                               \
-    _cmdClass *theCmd = new _cmdClass(_styleTag);                 \
-    aCommandTable->RegisterCommand(                               \
-        _cmdName, static_cast<nsIControllerCommand *>(theCmd));   \
-  }
-
-#define NS_REGISTER_TAG_COMMAND(_cmdClass, _cmdName, _tagName)  \
-  {                                                             \
-    _cmdClass *theCmd = new _cmdClass(_tagName);                \
-    aCommandTable->RegisterCommand(                             \
-        _cmdName, static_cast<nsIControllerCommand *>(theCmd)); \
+#define NS_REGISTER_COMMAND(_cmdClass, _cmdName)                        \
+  {                                                                     \
+    aCommandTable->RegisterCommand(                                     \
+        _cmdName,                                                       \
+        static_cast<nsIControllerCommand *>(_cmdClass::GetInstance())); \
   }
 
 // static
 nsresult HTMLEditorController::RegisterEditorDocStateCommands(
     nsControllerCommandTable *aCommandTable) {
   // observer commands for document state
-  NS_REGISTER_FIRST_COMMAND(DocumentStateCommand, "obs_documentCreated")
-  NS_REGISTER_NEXT_COMMAND(DocumentStateCommand, "obs_documentWillBeDestroyed")
-  NS_REGISTER_LAST_COMMAND(DocumentStateCommand, "obs_documentLocationChanged")
+  NS_REGISTER_COMMAND(DocumentStateCommand, "obs_documentCreated")
+  NS_REGISTER_COMMAND(DocumentStateCommand, "obs_documentWillBeDestroyed")
+  NS_REGISTER_COMMAND(DocumentStateCommand, "obs_documentLocationChanged")
 
   // commands that may get or change state
-  NS_REGISTER_FIRST_COMMAND(SetDocumentStateCommand, "cmd_setDocumentModified")
-  NS_REGISTER_NEXT_COMMAND(SetDocumentStateCommand, "cmd_setDocumentUseCSS")
-  NS_REGISTER_NEXT_COMMAND(SetDocumentStateCommand, "cmd_setDocumentReadOnly")
-  NS_REGISTER_NEXT_COMMAND(SetDocumentStateCommand, "cmd_insertBrOnReturn")
-  NS_REGISTER_NEXT_COMMAND(SetDocumentStateCommand,
-                           "cmd_defaultParagraphSeparator")
-  NS_REGISTER_NEXT_COMMAND(SetDocumentStateCommand, "cmd_enableObjectResizing")
-  NS_REGISTER_NEXT_COMMAND(SetDocumentStateCommand,
-                           "cmd_enableInlineTableEditing")
-  NS_REGISTER_LAST_COMMAND(SetDocumentStateCommand,
-                           "cmd_enableAbsolutePositionEditing")
+  NS_REGISTER_COMMAND(SetDocumentStateCommand, "cmd_setDocumentModified")
+  NS_REGISTER_COMMAND(SetDocumentStateCommand, "cmd_setDocumentUseCSS")
+  NS_REGISTER_COMMAND(SetDocumentStateCommand, "cmd_setDocumentReadOnly")
+  NS_REGISTER_COMMAND(SetDocumentStateCommand, "cmd_insertBrOnReturn")
+  NS_REGISTER_COMMAND(SetDocumentStateCommand, "cmd_defaultParagraphSeparator")
+  NS_REGISTER_COMMAND(SetDocumentStateCommand, "cmd_enableObjectResizing")
+  NS_REGISTER_COMMAND(SetDocumentStateCommand, "cmd_enableInlineTableEditing")
+  NS_REGISTER_COMMAND(SetDocumentStateCommand,
+                      "cmd_enableAbsolutePositionEditing")
 
   return NS_OK;
 }
 
 // static
 nsresult HTMLEditorController::RegisterHTMLEditorCommands(
     nsControllerCommandTable *aCommandTable) {
   // Edit menu
-  NS_REGISTER_ONE_COMMAND(PasteNoFormattingCommand, "cmd_pasteNoFormatting");
+  NS_REGISTER_COMMAND(PasteNoFormattingCommand, "cmd_pasteNoFormatting");
 
   // indent/outdent
-  NS_REGISTER_ONE_COMMAND(IndentCommand, "cmd_indent");
-  NS_REGISTER_ONE_COMMAND(OutdentCommand, "cmd_outdent");
+  NS_REGISTER_COMMAND(IndentCommand, "cmd_indent");
+  NS_REGISTER_COMMAND(OutdentCommand, "cmd_outdent");
 
   // Styles
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_bold", nsGkAtoms::b);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_italic", nsGkAtoms::i);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_underline",
-                            nsGkAtoms::u);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_tt", nsGkAtoms::tt);
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_bold");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_italic");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_underline");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_tt");
+
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_strikethrough");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_superscript");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_subscript");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_nobreak");
 
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_strikethrough",
-                            nsGkAtoms::strike);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_superscript",
-                            nsGkAtoms::sup);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_subscript",
-                            nsGkAtoms::sub);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_nobreak",
-                            nsGkAtoms::nobr);
-
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_em", nsGkAtoms::em);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_strong",
-                            nsGkAtoms::strong);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_cite", nsGkAtoms::cite);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_abbr", nsGkAtoms::abbr);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_acronym",
-                            nsGkAtoms::acronym);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_code", nsGkAtoms::code);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_samp", nsGkAtoms::samp);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_var", nsGkAtoms::var);
-  NS_REGISTER_STYLE_COMMAND(StyleUpdatingCommand, "cmd_removeLinks",
-                            nsGkAtoms::href);
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_em");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_strong");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_cite");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_abbr");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_acronym");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_code");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_samp");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_var");
+  NS_REGISTER_COMMAND(StyleUpdatingCommand, "cmd_removeLinks");
 
   // lists
-  NS_REGISTER_STYLE_COMMAND(ListCommand, "cmd_ol", nsGkAtoms::ol);
-  NS_REGISTER_STYLE_COMMAND(ListCommand, "cmd_ul", nsGkAtoms::ul);
-  NS_REGISTER_STYLE_COMMAND(ListItemCommand, "cmd_dt", nsGkAtoms::dt);
-  NS_REGISTER_STYLE_COMMAND(ListItemCommand, "cmd_dd", nsGkAtoms::dd);
-  NS_REGISTER_ONE_COMMAND(RemoveListCommand, "cmd_removeList");
+  NS_REGISTER_COMMAND(ListCommand, "cmd_ol");
+  NS_REGISTER_COMMAND(ListCommand, "cmd_ul");
+  NS_REGISTER_COMMAND(ListItemCommand, "cmd_dt");
+  NS_REGISTER_COMMAND(ListItemCommand, "cmd_dd");
+  NS_REGISTER_COMMAND(RemoveListCommand, "cmd_removeList");
 
   // format stuff
-  NS_REGISTER_ONE_COMMAND(ParagraphStateCommand, "cmd_paragraphState");
-  NS_REGISTER_ONE_COMMAND(FontFaceStateCommand, "cmd_fontFace");
-  NS_REGISTER_ONE_COMMAND(FontSizeStateCommand, "cmd_fontSize");
-  NS_REGISTER_ONE_COMMAND(FontColorStateCommand, "cmd_fontColor");
-  NS_REGISTER_ONE_COMMAND(BackgroundColorStateCommand, "cmd_backgroundColor");
-  NS_REGISTER_ONE_COMMAND(HighlightColorStateCommand, "cmd_highlight");
+  NS_REGISTER_COMMAND(ParagraphStateCommand, "cmd_paragraphState");
+  NS_REGISTER_COMMAND(FontFaceStateCommand, "cmd_fontFace");
+  NS_REGISTER_COMMAND(FontSizeStateCommand, "cmd_fontSize");
+  NS_REGISTER_COMMAND(FontColorStateCommand, "cmd_fontColor");
+  NS_REGISTER_COMMAND(BackgroundColorStateCommand, "cmd_backgroundColor");
+  NS_REGISTER_COMMAND(HighlightColorStateCommand, "cmd_highlight");
 
-  NS_REGISTER_ONE_COMMAND(AlignCommand, "cmd_align");
-  NS_REGISTER_ONE_COMMAND(RemoveStylesCommand, "cmd_removeStyles");
+  NS_REGISTER_COMMAND(AlignCommand, "cmd_align");
+  NS_REGISTER_COMMAND(RemoveStylesCommand, "cmd_removeStyles");
 
-  NS_REGISTER_ONE_COMMAND(IncreaseFontSizeCommand, "cmd_increaseFont");
-  NS_REGISTER_ONE_COMMAND(DecreaseFontSizeCommand, "cmd_decreaseFont");
+  NS_REGISTER_COMMAND(IncreaseFontSizeCommand, "cmd_increaseFont");
+  NS_REGISTER_COMMAND(DecreaseFontSizeCommand, "cmd_decreaseFont");
 
   // Insert content
-  NS_REGISTER_ONE_COMMAND(InsertHTMLCommand, "cmd_insertHTML");
-  NS_REGISTER_TAG_COMMAND(InsertTagCommand, "cmd_insertLinkNoUI", nsGkAtoms::a);
-  NS_REGISTER_TAG_COMMAND(InsertTagCommand, "cmd_insertImageNoUI",
-                          nsGkAtoms::img);
-  NS_REGISTER_TAG_COMMAND(InsertTagCommand, "cmd_insertHR", nsGkAtoms::hr);
+  NS_REGISTER_COMMAND(InsertHTMLCommand, "cmd_insertHTML");
+  NS_REGISTER_COMMAND(InsertTagCommand, "cmd_insertLinkNoUI");
+  NS_REGISTER_COMMAND(InsertTagCommand, "cmd_insertImageNoUI");
+  NS_REGISTER_COMMAND(InsertTagCommand, "cmd_insertHR");
 
-  NS_REGISTER_ONE_COMMAND(AbsolutePositioningCommand, "cmd_absPos");
-  NS_REGISTER_ONE_COMMAND(DecreaseZIndexCommand, "cmd_decreaseZIndex");
-  NS_REGISTER_ONE_COMMAND(IncreaseZIndexCommand, "cmd_increaseZIndex");
+  NS_REGISTER_COMMAND(AbsolutePositioningCommand, "cmd_absPos");
+  NS_REGISTER_COMMAND(DecreaseZIndexCommand, "cmd_decreaseZIndex");
+  NS_REGISTER_COMMAND(IncreaseZIndexCommand, "cmd_increaseZIndex");
 
   return NS_OK;
 }
 
+// static
+void HTMLEditorController::Shutdown() {
+  // EditorDocStateCommands
+  DocumentStateCommand::Shutdown();
+  SetDocumentStateCommand::Shutdown();
+
+  // HTMLEditorCommands
+  PasteNoFormattingCommand::Shutdown();
+  IndentCommand::Shutdown();
+  OutdentCommand::Shutdown();
+  StyleUpdatingCommand::Shutdown();
+  ListCommand::Shutdown();
+  ListItemCommand::Shutdown();
+  RemoveListCommand::Shutdown();
+  ParagraphStateCommand::Shutdown();
+  FontFaceStateCommand::Shutdown();
+  FontSizeStateCommand::Shutdown();
+  FontColorStateCommand::Shutdown();
+  BackgroundColorStateCommand::Shutdown();
+  HighlightColorStateCommand::Shutdown();
+  AlignCommand::Shutdown();
+  RemoveStylesCommand::Shutdown();
+  IncreaseFontSizeCommand::Shutdown();
+  DecreaseFontSizeCommand::Shutdown();
+  InsertHTMLCommand::Shutdown();
+  InsertTagCommand::Shutdown();
+  AbsolutePositioningCommand::Shutdown();
+  DecreaseZIndexCommand::Shutdown();
+  IncreaseZIndexCommand::Shutdown();
+}
+
 }  // namespace mozilla
--- a/editor/libeditor/HTMLEditorController.h
+++ b/editor/libeditor/HTMLEditorController.h
@@ -13,13 +13,14 @@ class nsControllerCommandTable;
 namespace mozilla {
 
 class HTMLEditorController final {
  public:
   static nsresult RegisterEditorDocStateCommands(
       nsControllerCommandTable* aCommandTable);
   static nsresult RegisterHTMLEditorCommands(
       nsControllerCommandTable* aCommandTable);
+  static void Shutdown();
 };
 
 }  // namespace mozilla
 
 #endif /* mozllla_HTMLEditorController_h__ */
--- a/editor/libeditor/HTMLEditorDocumentCommands.cpp
+++ b/editor/libeditor/HTMLEditorDocumentCommands.cpp
@@ -28,23 +28,27 @@ class nsISupports;
 // defines
 #define STATE_ENABLED "state_enabled"
 #define STATE_ALL "state_all"
 #define STATE_ATTRIBUTE "state_attribute"
 #define STATE_DATA "state_data"
 
 namespace mozilla {
 
-/**
+/*****************************************************************************
+ * mozilla::SetDocumentStateCommand
+ *
  *  Commands for document state that may be changed via doCommandParams
  *  As of 11/11/02, this is just "cmd_setDocumentModified"
- *  Note that you can use the same command class, nsSetDocumentStateCommand,
+ *  Note that you can use the same command class, SetDocumentStateCommand,
  *    for more than one of this type of command
  *    We check the input command param for different behavior
- */
+ *****************************************************************************/
+
+StaticRefPtr<SetDocumentStateCommand> SetDocumentStateCommand::sInstance;
 
 NS_IMETHODIMP
 SetDocumentStateCommand::IsCommandEnabled(const char* aCommandName,
                                           nsISupports* refCon,
                                           bool* outCmdEnabled) {
   if (NS_WARN_IF(!outCmdEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
@@ -399,23 +403,25 @@ SetDocumentStateCommand::GetCommandState
     }
     return params->SetBool(STATE_ALL,
                            htmlEditor->IsAbsolutePositionEditorEnabled());
   }
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-/**
+/*****************************************************************************
+ * mozilla::DocumentStateCommand
+ *
  * Commands just for state notification
  *  As of 11/21/02, possible commands are:
  *    "obs_documentCreated"
  *    "obs_documentWillBeDestroyed"
  *    "obs_documentLocationChanged"
- *  Note that you can use the same command class, nsDocumentStateCommand
+ *  Note that you can use the same command class, DocumentStateCommand
  *    for these or future observer commands.
  *    We check the input command param for different behavior
  *
  *  How to use:
  *  1. Get the nsCommandManager for the current editor
  *  2. Implement an nsIObserve object, e.g:
  *
  *    void Observe(
@@ -432,18 +438,19 @@ SetDocumentStateCommand::GetCommandState
  *
  *  RefPtr<nsCommandManager> commandManager = mDocShell->GetCommandManager();
  *  commandManager->CommandStatusChanged(obs_documentCreated);
  *
  *  5. Use GetCommandStateParams() to obtain state information
  *     e.g., any creation state codes when creating an editor are
  *     supplied for "obs_documentCreated" command in the
  *     "state_data" param's value
- *
- */
+ *****************************************************************************/
+
+StaticRefPtr<DocumentStateCommand> DocumentStateCommand::sInstance;
 
 NS_IMETHODIMP
 DocumentStateCommand::IsCommandEnabled(const char* aCommandName,
                                        nsISupports* refCon,
                                        bool* outCmdEnabled) {
   if (NS_WARN_IF(!outCmdEnabled)) {
     return NS_ERROR_INVALID_ARG;
   }
--- a/gfx/webrender_bindings/RenderAndroidSurfaceTextureHostOGL.cpp
+++ b/gfx/webrender_bindings/RenderAndroidSurfaceTextureHostOGL.cpp
@@ -14,28 +14,24 @@ namespace mozilla {
 namespace wr {
 
 RenderAndroidSurfaceTextureHostOGL::RenderAndroidSurfaceTextureHostOGL(
     const java::GeckoSurfaceTexture::GlobalRef& aSurfTex, gfx::IntSize aSize,
     gfx::SurfaceFormat aFormat, bool aContinuousUpdate)
     : mSurfTex(aSurfTex),
       mSize(aSize),
       mContinuousUpdate(aContinuousUpdate),
-      mIsPrepared(false),
+      mPrepareStatus(STATUS_NONE),
       mAttachedToGLContext(false) {
   MOZ_COUNT_CTOR_INHERITED(RenderAndroidSurfaceTextureHostOGL,
                            RenderTextureHostOGL);
 
   if (mSurfTex) {
     mSurfTex->IncrementUse();
   }
-
-  if (mSurfTex && !mSurfTex->IsSingleBuffer()) {
-    mIsPrepared = true;
-  }
 }
 
 RenderAndroidSurfaceTextureHostOGL::~RenderAndroidSurfaceTextureHostOGL() {
   MOZ_ASSERT(RenderThread::IsInRenderThread());
   MOZ_COUNT_DTOR_INHERITED(RenderAndroidSurfaceTextureHostOGL,
                            RenderTextureHostOGL);
   DeleteTextureHandle();
   if (mSurfTex) {
@@ -82,20 +78,23 @@ wr::WrExternalImage RenderAndroidSurface
     ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0,
                                  LOCAL_GL_TEXTURE_EXTERNAL_OES,
                                  mSurfTex->GetTexName(), aRendering);
   }
 
   if (mContinuousUpdate) {
     MOZ_ASSERT(!mSurfTex->IsSingleBuffer());
     mSurfTex->UpdateTexImage();
-  } else if (!mSurfTex->IsSingleBuffer()) {
-    // Always calling UpdateTexImage() is not good.
-    // XXX Bug 1543846 will update this.
+  } else if (mPrepareStatus == STATUS_PREPARE_NEEDED) {
+    MOZ_ASSERT(!mSurfTex->IsSingleBuffer());
+    // When SurfaceTexture is not single buffer mode, call UpdateTexImage() once
+    // just before rendering. During playing video, one SurfaceTexture is used
+    // for all RenderAndroidSurfaceTextureHostOGLs of video.
     mSurfTex->UpdateTexImage();
+    mPrepareStatus = STATUS_PREPARED;
   }
 
   return NativeTextureToWrExternalImage(mSurfTex->GetTexName(), 0, 0,
                                         mSize.width, mSize.height);
 }
 
 void RenderAndroidSurfaceTextureHostOGL::Unlock() {}
 
@@ -130,38 +129,55 @@ bool RenderAndroidSurfaceTextureHostOGL:
     }
   }
 
   mAttachedToGLContext = true;
   return true;
 }
 
 void RenderAndroidSurfaceTextureHostOGL::PrepareForUse() {
-  // When SurfaceTexture is single buffer mode, UpdateTexImage neeeds to be
+  // When SurfaceTexture is single buffer mode, UpdateTexImage needs to be
   // called only once for each publish. If UpdateTexImage is called more
   // than once, it causes hang on puglish side. And UpdateTexImage needs to
   // be called on render thread, since the SurfaceTexture is consumed on render
   // thread.
 
   MOZ_ASSERT(RenderThread::IsInRenderThread());
+  MOZ_ASSERT(mPrepareStatus == STATUS_NONE);
 
   EnsureAttachedToGLContext();
 
-  if (mSurfTex && mSurfTex->IsSingleBuffer() && !mIsPrepared) {
+  if (mContinuousUpdate) {
+    return;
+  }
+
+  mPrepareStatus = STATUS_PREPARE_NEEDED;
+
+  if (mSurfTex && mSurfTex->IsSingleBuffer()) {
+    // When SurfaceTexture is single buffer mode, it is OK to call
+    // UpdateTexImage() here.
     mSurfTex->UpdateTexImage();
-    mIsPrepared = true;
+    mPrepareStatus = STATUS_PREPARED;
   }
 }
 
 void RenderAndroidSurfaceTextureHostOGL::NotifyNotUsed() {
   MOZ_ASSERT(RenderThread::IsInRenderThread());
 
   EnsureAttachedToGLContext();
 
-  if (mSurfTex && mSurfTex->IsSingleBuffer() && mIsPrepared) {
+  if (mSurfTex && mSurfTex->IsSingleBuffer() &&
+      mPrepareStatus == STATUS_PREPARED) {
+    // Release SurfaceTexture's buffer to client side.
     mGL->MakeCurrent();
     mSurfTex->ReleaseTexImage();
-    mIsPrepared = false;
+  } else if (mSurfTex && mPrepareStatus == STATUS_PREPARE_NEEDED) {
+    // This could happen when video frame was skipped. UpdateTexImage() neeeds
+    // to be called for adjusting SurfaceTexture's buffer status.
+    MOZ_ASSERT(!mSurfTex->IsSingleBuffer());
+    mSurfTex->UpdateTexImage();
   }
+
+  mPrepareStatus = STATUS_NONE;
 }
 
 }  // namespace wr
 }  // namespace mozilla
--- a/gfx/webrender_bindings/RenderAndroidSurfaceTextureHostOGL.h
+++ b/gfx/webrender_bindings/RenderAndroidSurfaceTextureHostOGL.h
@@ -31,21 +31,25 @@ class RenderAndroidSurfaceTextureHostOGL
   virtual void PrepareForUse() override;
   virtual void NotifyNotUsed() override;
 
  private:
   virtual ~RenderAndroidSurfaceTextureHostOGL();
   void DeleteTextureHandle();
   bool EnsureAttachedToGLContext();
 
+  enum PrepareStatus { STATUS_NONE, STATUS_PREPARE_NEEDED, STATUS_PREPARED };
+
   const mozilla::java::GeckoSurfaceTexture::GlobalRef mSurfTex;
   const gfx::IntSize mSize;
+  // mContinuousUpdate was used for rendering video in the past.
+  // It is not used on current gecko.
   const bool mContinuousUpdate;
   // XXX const bool mIgnoreTransform;
-  bool mIsPrepared;
+  PrepareStatus mPrepareStatus;
   bool mAttachedToGLContext;
 
   RefPtr<gl::GLContext> mGL;
 };
 
 }  // namespace wr
 }  // namespace mozilla
 
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1000,72 +1000,140 @@ void BaselineCompilerCodeGen::emitPreIni
   } else {
     masm.storePtr(nonFunctionEnv, frame.addressOfEnvironmentChain());
   }
 }
 
 template <>
 void BaselineInterpreterCodeGen::emitPreInitEnvironmentChain(
     Register nonFunctionEnv) {
-  MOZ_CRASH("NYI: interpreter emitPreInitEnvironmentChain");
+  Label notFunction, done;
+  masm.branchTestPtr(Assembler::NonZero, frame.addressOfCalleeToken(),
+                     Imm32(CalleeTokenScriptBit), &notFunction);
+  {
+    masm.storePtr(ImmPtr(nullptr), frame.addressOfEnvironmentChain());
+    masm.jump(&done);
+  }
+  masm.bind(&notFunction);
+  { masm.storePtr(nonFunctionEnv, frame.addressOfEnvironmentChain()); }
+  masm.bind(&done);
+}
+
+template <>
+void BaselineCompilerCodeGen::emitInitFrameFields() {
+  masm.store32(Imm32(0), frame.addressOfFlags());
+}
+
+template <>
+void BaselineInterpreterCodeGen::emitInitFrameFields() {
+  MOZ_CRASH("NYI: interpreter emitInitFrameFields");
+}
+
+template <>
+template <typename F1, typename F2>
+bool BaselineCompilerCodeGen::initEnvironmentChainHelper(
+    const F1& initFunctionEnv, const F2& initGlobalOrEvalEnv,
+    Register scratch) {
+  if (handler.function()) {
+    return initFunctionEnv();
+  }
+  if (handler.module()) {
+    return true;
+  }
+  return initGlobalOrEvalEnv();
 }
 
 template <>
-bool BaselineCompilerCodeGen::initEnvironmentChain() {
+template <typename F1, typename F2>
+bool BaselineInterpreterCodeGen::initEnvironmentChainHelper(
+    const F1& initFunctionEnv, const F2& initGlobalOrEvalEnv,
+    Register scratch) {
+  // There are three cases:
+  //
+  // 1) Function script: use code emitted by initFunctionEnv.
+  // 2) Module script: no-op.
+  // 3) Global or eval script: use code emitted by initGlobalOrEvalEnv.
+
+  Label notFunction, done;
+  masm.loadPtr(frame.addressOfCalleeToken(), scratch);
+  masm.branchTestPtr(Assembler::NonZero, scratch, Imm32(CalleeTokenScriptBit),
+                     &notFunction);
+  {
+    if (!initFunctionEnv()) {
+      return false;
+    }
+    masm.jump(&done);
+  }
+  masm.bind(&notFunction);
+  {
+    masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), scratch);
+    masm.branchTest32(Assembler::NonZero,
+                      Address(scratch, JSScript::offsetOfImmutableFlags()),
+                      Imm32(uint32_t(JSScript::ImmutableFlags::IsModule)),
+                      &done);
+    {
+      if (!initGlobalOrEvalEnv()) {
+        return false;
+      }
+    }
+  }
+
+  masm.bind(&done);
+  return true;
+}
+
+template <typename Handler>
+bool BaselineCodeGen<Handler>::initEnvironmentChain() {
   CallVMPhase phase = POST_INITIALIZE;
   if (handler.needsEarlyStackCheck()) {
     phase = CHECK_OVER_RECURSED;
   }
 
-  RootedFunction fun(cx, handler.function());
-  if (fun) {
+  auto initFunctionEnv = [this, phase]() {
     // Use callee->environment as env chain. Note that we do this also
     // for needsSomeEnvironmentObject functions, so that the env chain
     // slot is properly initialized if the call triggers GC.
     Register callee = R0.scratchReg();
     Register scope = R1.scratchReg();
     masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), callee);
     masm.loadPtr(Address(callee, JSFunction::offsetOfEnvironment()), scope);
     masm.storePtr(scope, frame.addressOfEnvironmentChain());
 
-    if (fun->needsFunctionEnvironmentObjects()) {
+    auto initEnv = [this, phase]() {
       // Call into the VM to create the proper environment objects.
       prepareVMCall();
 
       masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
       pushArg(R0.scratchReg());
 
       using Fn = bool (*)(JSContext*, BaselineFrame*);
-      if (!callVMNonOp<Fn, jit::InitFunctionEnvironmentObjects>(phase)) {
-        return false;
-      }
-    }
-  } else if (!handler.module()) {
+      return callVMNonOp<Fn, jit::InitFunctionEnvironmentObjects>(phase);
+    };
+    return emitTestScriptFlag(
+        JSScript::ImmutableFlags::NeedsFunctionEnvironmentObjects, true,
+        initEnv, R2.scratchReg());
+  };
+
+  auto initGlobalOrEvalEnv = [this, phase]() {
     // EnvironmentChain pointer in BaselineFrame has already been initialized
     // in prologue, but we need to check for redeclaration errors in global and
     // eval scripts.
 
     prepareVMCall();
 
     pushScriptArg();
     masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
     pushArg(R0.scratchReg());
 
     using Fn = bool (*)(JSContext*, HandleObject, HandleScript);
-    if (!callVMNonOp<Fn, js::CheckGlobalOrEvalDeclarationConflicts>(phase)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-template <>
-bool BaselineInterpreterCodeGen::initEnvironmentChain() {
-  MOZ_CRASH("NYI: interpreter initEnvironmentChain");
+    return callVMNonOp<Fn, js::CheckGlobalOrEvalDeclarationConflicts>(phase);
+  };
+
+  return initEnvironmentChainHelper(initFunctionEnv, initGlobalOrEvalEnv,
+                                    R2.scratchReg());
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitInterruptCheck() {
   frame.syncStack(0);
 
   Label done;
   masm.branch32(Assembler::Equal, AbsoluteAddress(cx->addressOfInterruptBits()),
@@ -6050,18 +6118,18 @@ bool BaselineCodeGen<Handler>::emitProlo
 
   masm.push(BaselineFrameReg);
   masm.moveStackPtrTo(BaselineFrameReg);
   masm.subFromStackPtr(Imm32(BaselineFrame::Size()));
 
   // Initialize BaselineFrame. For eval scripts, the env chain
   // is passed in R1, so we have to be careful not to clobber it.
 
-  // Initialize BaselineFrame::flags.
-  masm.store32(Imm32(0), frame.addressOfFlags());
+  // Initialize BaselineFrame::flags and interpreter fields.
+  emitInitFrameFields();
 
   // Handle env chain pre-initialization (in case GC gets run
   // during stack check).  For global and eval scripts, the env
   // chain is in R1.  For function scripts, the env chain is in
   // the callee, nullptr is stored for now so that GC doesn't choke
   // on a bogus EnvironmentChain value in the frame.
   emitPreInitEnvironmentChain(R1.scratchReg());
 
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -471,21 +471,27 @@ class BaselineCodeGen {
   Address getEnvironmentCoordinateAddress(Register reg);
 
   MOZ_MUST_USE bool emitPrologue();
   MOZ_MUST_USE bool emitEpilogue();
   MOZ_MUST_USE bool emitOutOfLinePostBarrierSlot();
   MOZ_MUST_USE bool emitStackCheck();
   MOZ_MUST_USE bool emitArgumentTypeChecks();
   MOZ_MUST_USE bool emitDebugPrologue();
+
+  template <typename F1, typename F2>
+  MOZ_MUST_USE bool initEnvironmentChainHelper(const F1& initFunctionEnv,
+                                               const F2& initGlobalOrEvalEnv,
+                                               Register scratch);
   MOZ_MUST_USE bool initEnvironmentChain();
 
   MOZ_MUST_USE bool emitTraceLoggerEnter();
   MOZ_MUST_USE bool emitTraceLoggerExit();
 
+  void emitInitFrameFields();
   void emitIsDebuggeeCheck();
   void emitInitializeLocals();
   void emitPreInitEnvironmentChain(Register nonFunctionEnv);
 
   void emitProfilerEnterFrame();
   void emitProfilerExitFrame();
 };
 
--- a/js/src/wasm/cranelift/Cargo.toml
+++ b/js/src/wasm/cranelift/Cargo.toml
@@ -3,19 +3,26 @@ name = "baldrdash"
 version = "0.1.0"
 authors = ["The Spidermonkey and Cranelift developers"]
 
 [lib]
 crate-type = ["rlib"]
 name = "baldrdash"
 
 [dependencies]
-cranelift-codegen = { version = "0.29.0", default-features = false }
-cranelift-wasm = "0.29.0"
-target-lexicon = "0.2.0"
+# The build system redirects the versions of cranelift-codegen and
+# cranelift-wasm to pinned commits. If you want to update Cranelift in Gecko,
+# you should update the following files:
+# - $TOP_LEVEL/Cargo.toml, look for the revision (rev) hashes of both cranelift
+# dependencies (codegen and wasm).
+# - $TOP_LEVEL/.cargo/config.in, look for the revision (rev) field of the
+# Cranelift source.
+cranelift-codegen = { version = "0.30", default-features = false }
+cranelift-wasm = "0.30"
+target-lexicon = "0.4.0"
 log = { version = "0.4.6", default-features = false, features = ["release_max_level_info"] }
 env_logger = "0.5.6"
 
 [build-dependencies]
 bindgen = {version = "0.49", default-features = false} # disable `logging` to reduce code size
 
 [features]
 default = ['cranelift-codegen/std']
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -941,16 +941,21 @@ void PresShell::Init(Document* aDocument
   mViewManager->SetPresShell(this);
 
   // Bind the context to the presentation shell.
   mPresContext = aPresContext;
   mPresContext->AttachPresShell(this);
 
   mPresContext->DeviceContext()->InitFontCache();
 
+  // FIXME(emilio, bug 1544185): Some Android code somehow depends on the shell
+  // being eagerly registered as a style flush observer. This shouldn't be
+  // needed otherwise.
+  EnsureStyleFlush();
+
   // Add the preference style sheet.
   UpdatePreferenceStyles();
 
   bool accessibleCaretEnabled =
       AccessibleCaretEnabled(mDocument->GetDocShell());
   if (accessibleCaretEnabled) {
     // Need to happen before nsFrameSelection has been set up.
     mAccessibleCaretEventHub = new AccessibleCaretEventHub(this);
--- a/layout/base/tests/marionette/manifest.ini
+++ b/layout/base/tests/marionette/manifest.ini
@@ -1,5 +1,4 @@
 [DEFAULT]
 run-if = buildapp == 'browser'
-skip-if = (os == "win" && processor == "aarch64") # aarch64 due to 1536278
 [test_accessiblecaret_cursor_mode.py]
 [test_accessiblecaret_selection_mode.py]
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -102,17 +102,19 @@
 #include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "TouchManager.h"
 #include "DecoderDoctorLogger.h"
 #include "MediaDecoder.h"
 #include "mozilla/ClearSiteData.h"
+#include "mozilla/EditorController.h"
 #include "mozilla/Fuzzyfox.h"
+#include "mozilla/HTMLEditorController.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StaticPresData.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamStorage.h"
 #include "mozilla/dom/U2FTokenManager.h"
 #ifdef OS_WIN
 #  include "mozilla/dom/WinWebAuthnManager.h"
 #endif
@@ -338,16 +340,18 @@ void nsLayoutStatics::Shutdown() {
   nsXULPopupManager::Shutdown();
 #endif
   UIDirectionManager::Shutdown();
   StorageObserver::Shutdown();
   txMozillaXSLTProcessor::Shutdown();
   Attr::Shutdown();
   PopupBlocker::Shutdown();
   IMEStateManager::Shutdown();
+  EditorController::Shutdown();
+  HTMLEditorController::Shutdown();
   nsMediaFeatures::Shutdown();
   nsHTMLDNSPrefetch::Shutdown();
   nsCSSRendering::Shutdown();
   StaticPresData::Shutdown();
 #ifdef DEBUG
   nsFrame::DisplayReflowShutdown();
 #endif
   nsCellMap::Shutdown();
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -4786,16 +4786,92 @@ nsresult nsCookieService::RemoveCookiesW
     }
   }
   DebugOnly<nsresult> rv = transaction.Commit();
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsCookieService::RemoveCookiesFromRootDomain(const nsACString &aHost,
+                                             const nsAString &aPattern) {
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  mozilla::OriginAttributesPattern pattern;
+  if (!pattern.Init(aPattern)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return RemoveCookiesFromRootDomain(aHost, pattern);
+}
+
+nsresult nsCookieService::RemoveCookiesFromRootDomain(
+    const nsACString &aHost, const mozilla::OriginAttributesPattern &aPattern) {
+  nsAutoCString host(aHost);
+  nsresult rv = NormalizeHost(host);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString baseDomain;
+  rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mDBState) {
+    NS_WARNING("No DBState! Profile already close?");
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  EnsureReadComplete(true);
+
+  AutoRestore<DBState *> savePrevDBState(mDBState);
+  mDBState = (aPattern.mPrivateBrowsingId.WasPassed() &&
+              aPattern.mPrivateBrowsingId.Value() > 0)
+                 ? mPrivateDBState
+                 : mDefaultDBState;
+
+  mozStorageTransaction transaction(mDBState->dbConn, false);
+  // Iterate the hash table of nsCookieEntry.
+  for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
+    nsCookieEntry *entry = iter.Get();
+
+    if (!baseDomain.Equals(entry->mBaseDomain)) {
+      continue;
+    }
+
+    if (!aPattern.Matches(entry->mOriginAttributes)) {
+      continue;
+    }
+
+    uint32_t cookiesCount = entry->GetCookies().Length();
+    for (nsCookieEntry::IndexType i = cookiesCount; i != 0; --i) {
+      nsListIter iter(entry, i - 1);
+      RefPtr<nsCookie> cookie = iter.Cookie();
+
+      bool hasRootDomain = false;
+      rv = mTLDService->HasRootDomain(cookie->Host(), aHost, &hasRootDomain);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (!hasRootDomain) {
+        continue;
+      }
+
+      // Remove the cookie.
+      RemoveCookieFromList(iter);
+
+      if (cookie) {
+        NotifyChanged(cookie, u"deleted");
+      }
+    }
+  }
+  rv = transaction.Commit();
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  return NS_OK;
+}
+
 // find an secure cookie specified by host and name
 bool nsCookieService::FindSecureCookie(const nsCookieKey &aKey,
                                        nsCookie *aCookie) {
   nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
   if (!entry) return false;
 
   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -382,16 +382,20 @@ class nsCookieService final : public nsI
    * with OriginAttributes parameter.
    * NOTE: this could be added to a public interface if we happen to need it.
    */
   nsresult Remove(const nsACString &aHost, const OriginAttributes &aAttrs,
                   const nsACString &aName, const nsACString &aPath,
                   bool aBlocked);
 
  protected:
+  nsresult RemoveCookiesFromRootDomain(
+      const nsACString &aHost,
+      const mozilla::OriginAttributesPattern &aPattern);
+
   // cached members.
   nsCOMPtr<nsICookiePermission> mPermissionService;
   nsCOMPtr<mozIThirdPartyUtil> mThirdPartyUtil;
   nsCOMPtr<nsIEffectiveTLDService> mTLDService;
   nsCOMPtr<nsIIDNService> mIDNService;
   nsCOMPtr<mozIStorageService> mStorageService;
 
   // we have two separate DB states: one for normal browsing and one for
--- a/netwerk/cookie/nsICookieManager.idl
+++ b/netwerk/cookie/nsICookieManager.idl
@@ -233,9 +233,18 @@ interface nsICookieManager : nsISupports
 
   /**
    * Remove all the cookies whose origin attributes matches aPattern
    *
    * @param aPattern origin attribute pattern in JSON format
    */
   void removeCookiesWithOriginAttributes(in AString aPattern,
                                          [optional] in AUTF8String aHost);
+
+  /**
+   * Remove all the cookies whose origin attributes matches aPattern and the
+   * host is the root domain of aHost.
+   *
+   * @param aHost the host to match the root domain
+   * @param aPattern origin attribute pattern in JSON format
+   */
+  void removeCookiesFromRootDomain(in AUTF8String aHost, in AString aPattern);
 };
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/playback/mitmproxy-recordings-raptor-outlook.manifest
@@ -0,0 +1,10 @@
+[
+  {
+    "size": 12179560,
+    "visibility": "public",
+    "digest": "24f431940afdf1687cab2dd3a2519203f73309535ab60c4f4275d53a5c9145875b8b48c739383318e3e5e52cb9ef4c29e475eec2c4d75e3871aa49938622597c",
+    "algorithm": "sha512",
+    "filename": "mitmproxy-tp6-outlook.zip",
+    "unpack": true
+  }
+]
--- a/testing/raptor/raptor/tests/raptor-tp6-9.ini
+++ b/testing/raptor/raptor/tests/raptor-tp6-9.ini
@@ -19,30 +19,48 @@ alert_on = fcp, loadtime
 
 [raptor-tp6-google-mail-firefox]
 apps = firefox
 test_url = https://mail.google.com/
 playback_pageset_manifest = mitmproxy-recordings-raptor-google-mail.manifest
 playback_recordings = google-mail.mp
 measure = fnbpaint, fcp, dcf, loadtime
 
+[raptor-tp6-outlook-firefox]
+apps = firefox
+test_url = https://outlook.live.com/mail/inbox
+playback_binary_manifest = mitmproxy-rel-bin-4.0.4-{platform}.manifest
+playback_pageset_manifest = mitmproxy-recordings-raptor-outlook.manifest
+playback_recordings = outlook.mp
+playback_version = 4.0.4
+measure = fnbpaint, fcp, dcf, loadtime
+
 [raptor-tp6-pinterest-firefox]
 apps = firefox
 test_url = https://pinterest.com/
 playback_pageset_manifest = mitmproxy-recordings-raptor-pinterest.manifest
 playback_recordings = pinterest.mp
 measure = fnbpaint, fcp, dcf, loadtime
 
 [raptor-tp6-google-mail-chrome]
 apps = chrome
 test_url = https://mail.google.com/
 playback_pageset_manifest = mitmproxy-recordings-raptor-google-mail.manifest
 playback_recordings = google-mail.mp
 measure = fcp, loadtime
 
+[raptor-tp6-outlook-chrome]
+apps = chrome
+test_url =https://outlook.live.com/mail/inbox
+playback_binary_manifest = mitmproxy-rel-bin-4.0.4-{platform}.manifest
+playback_pageset_manifest = mitmproxy-recordings-raptor-outlook.manifest
+playback_recordings = outlook.mp
+playback_version = 4.0.4
+measure = fcp, loadtime
+
 [raptor-tp6-pinterest-chrome]
 apps = chrome
 test_url = https://pinterest.com/
 playback_pageset_manifest = mitmproxy-recordings-raptor-pinterest.manifest
 playback_recordings = pinterest.mp
 measure = fcp, loadtime
 
 [raptor-tp6-google-mail-chromium]
--- a/testing/raptor/webext/raptor/manifest.json
+++ b/testing/raptor/webext/raptor/manifest.json
@@ -27,16 +27,17 @@
                   "*://*.espn.com/*",
                   "*://*.facebook.com/*",
                   "*://*.google.com/*",
                   "*://*.imdb.com/*",
                   "*://*.imgur.com/*",
                   "*://*.instagram.com/*",
                   "*://*.jianshu.com/*",
                   "*://*.linkedin.com/*",
+                  "*://*.live.com/*",
                   "*://*.microsoft.com/*",
                   "*://*.paypal.com/*",
                   "*://*.pinterest.com/*",
                   "*://*.reddit.com/*",
                   "*://*.stackoverflow.com/*",
                   "*://*.tumblr.com/*",
                   "*://*.twitter.com/*",
                   "*://*.vice.com/*",
--- a/third_party/rust/cranelift-bforest/.cargo-checksum.json
+++ b/third_party/rust/cranelift-bforest/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"d6c5cca60972e64e1abb435d2af6bf8af2fec2d5988d0fda9827f6bba1f6a47c","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"af367c67340fa7f6fb9a35b0aa637dcf303957f7ae7427a5f4f6356801c8bb04","src/lib.rs":"1b23abbfe5850a4cd77ae6ae5dcfc2f678ef36b4032fd7496f2b333c51e63301","src/map.rs":"5d891d62814941e19dfc88ff36538efa3da5479f3f97de8219a6f610c9a1ee32","src/node.rs":"e620c64e78488035f11723b14892c7986c06ad37dc5b115a35a453ff1ae66ca3","src/path.rs":"4868e59ff67db1c504747e4b7e202dd20c9da4cbd73d9fa82d53e5f3406dbb78","src/pool.rs":"6090f8c0e0da16ebee0e31bca66392d0075b3aff529d30d4e716fa20cd0aef99","src/set.rs":"b411158f813a310c7a6c337d4ada3bf0a021088c443875dc25233415dcbe0633"},"package":"a3a25dfe4a54449df63d592f2f6346a80350ac835d4be4bacb73c20b034ef763"}
\ No newline at end of file
+{"files":{"Cargo.toml":"4fb2be5a108736ec2eeb257fd9c90d7e4384321e34eaef0fc7c5517a8e096650","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"af367c67340fa7f6fb9a35b0aa637dcf303957f7ae7427a5f4f6356801c8bb04","src/lib.rs":"1b23abbfe5850a4cd77ae6ae5dcfc2f678ef36b4032fd7496f2b333c51e63301","src/map.rs":"5d891d62814941e19dfc88ff36538efa3da5479f3f97de8219a6f610c9a1ee32","src/node.rs":"e620c64e78488035f11723b14892c7986c06ad37dc5b115a35a453ff1ae66ca3","src/path.rs":"4868e59ff67db1c504747e4b7e202dd20c9da4cbd73d9fa82d53e5f3406dbb78","src/pool.rs":"6090f8c0e0da16ebee0e31bca66392d0075b3aff529d30d4e716fa20cd0aef99","src/set.rs":"b411158f813a310c7a6c337d4ada3bf0a021088c443875dc25233415dcbe0633"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cranelift-bforest/Cargo.toml
+++ b/third_party/rust/cranelift-bforest/Cargo.toml
@@ -1,37 +1,24 @@
-# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
-#
-# When uploading crates to the registry Cargo will automatically
-# "normalize" Cargo.toml files for maximal compatibility
-# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g. crates.io) dependencies
-#
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
-
 [package]
-edition = "2018"
+authors = ["The Cranelift Project Developers"]
 name = "cranelift-bforest"
-version = "0.29.0"
-authors = ["The Cranelift Project Developers"]
+version = "0.30.0"
 description = "A forest of B+-trees"
+license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://cranelift.readthedocs.io/"
+repository = "https://github.com/CraneStation/cranelift"
+categories = ["no-std"]
 readme = "README.md"
 keywords = ["btree", "forest", "set", "map"]
-categories = ["no-std"]
-license = "Apache-2.0 WITH LLVM-exception"
-repository = "https://github.com/CraneStation/cranelift"
-[dependencies.cranelift-entity]
-version = "0.29.0"
-default-features = false
+edition = "2018"
+
+[dependencies]
+cranelift-entity = { path = "../cranelift-entity", version = "0.30.0", default-features = false }
 
 [features]
-core = []
 default = ["std"]
 std = ["cranelift-entity/std"]
-[badges.maintenance]
-status = "experimental"
+core = []
 
-[badges.travis-ci]
-repository = "CraneStation/cranelift"
+[badges]
+maintenance = { status = "experimental" }
+travis-ci = { repository = "CraneStation/cranelift" }
--- 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":"f5ea16920dd3c3f9c1ef903e26b10a913cafb5ac30eb36deabca977de04a62ae","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/cdsl/isa.rs":"d3ddfc8bd3d691df034a1bacfa27b3e29eb2e7a30923508fa5c7af8d89e8a962","src/cdsl/mod.rs":"66ac1b5d095e431bcab88c4b9c5b1492a5d1ca87bcb9c9c3e544ede05b2ba925","src/cdsl/regs.rs":"b99f24c3ecb46691625dc177b4e18d53e02265bc85a2f827a8d18381fe8f39bb","src/cdsl/settings.rs":"4ddeadf1542cc2ddec0f9e6c22d1637050da519586cd9fec0243c3eab9619f82","src/cdsl/types.rs":"981ebe748973bdf2dee00fa71784f6dcaa6c7648442665f34a59ad97a05fe888","src/constant_hash.rs":"b8acd3f8712a4999819d9d9beced2938d9940a5748ba016c182f1132d97eefab","src/error.rs":"5110a4e3c1e97396ba02d9f5abbb8af4b586f0cc4d33a5c2473f1718cc4bef05","src/gen_registers.rs":"75bbbc0f8dd546c88ed52f350175656300e35e871382a7508e7123e32d4bee1e","src/gen_settings.rs":"4689ede4e460bfcc19511c1055ba359b52f248f4a6d3afd62b1d23bc493b37a1","src/gen_types.rs":"3935da6c6a53f9332e06f74bc3a46270656b4d4231ad28ed2648d7b1d2774e90","src/isa/arm32/mod.rs":"f5b0cbbb2f6c7f00bb9a9bc6f0b1120477ff7ff683a95a6cdbbeed1677b0c9c8","src/isa/arm64/mod.rs":"c234b0df3d36d6d8765ead548e43b5304480e79da9697e14f9d98525919921b3","src/isa/mod.rs":"7038e3aa629afc28707fea379237d3c161ab459d552160438ac75e1137c6246a","src/isa/riscv/mod.rs":"322220fa67cf8623eeb27c7d23f3cc34e05873860248ae99fd02af452c232383","src/isa/x86/mod.rs":"c9183448ffe378e599ec7dc6ae7180c97d3e11d15d7644b93eb1e4a3543222f2","src/lib.rs":"4c73b35cbd68aab9b9c8c86bb71f67555e0e15f36a22101e086a346b01ee8cfb","src/shared/mod.rs":"87b55c291c5e73a9d7cd9a0ebfc8e59501956195268673d0d980de58694f4741","src/shared/settings.rs":"bc6a15221d688bf63114c53493d31070860eb7fae208596374488404a65ee41a","src/shared/types.rs":"a3e449db1f515d268f3ad21301740ba415444d399f8433dbc48979f78557f66a","src/srcgen.rs":"72435db1e0c984d95c5c5aa758907ed79eaec41ca3203ac661c6acd64c19acce","src/unique_table.rs":"f6041df1fa85f2a1ee914b84791e80165a0858a6253c212eaa99ff67cb56af26"},"package":"3e60ce3551e8172c966fbc6d9bfb90111d5d1cf37306c37dd7527467afe317d1"}
\ No newline at end of file
+{"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_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":"7ddb2d1f1a80d3dc3a7434309a6e0890dd054e88c549c20d2959236ffe986bd9","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
--- a/third_party/rust/cranelift-codegen-meta/Cargo.toml
+++ b/third_party/rust/cranelift-codegen-meta/Cargo.toml
@@ -1,28 +1,22 @@
-# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
-#
-# When uploading crates to the registry Cargo will automatically
-# "normalize" Cargo.toml files for maximal compatibility
-# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g. crates.io) dependencies
-#
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
-
 [package]
-edition = "2018"
 name = "cranelift-codegen-meta"
-version = "0.29.0"
 authors = ["The Cranelift Project Developers"]
+version = "0.30.0"
 description = "Metaprogram for cranelift-codegen code generator library"
-readme = "README.md"
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/CraneStation/cranelift"
-[dependencies.cranelift-entity]
-version = "0.29.0"
-[badges.maintenance]
-status = "experimental"
+readme = "README.md"
+edition = "2018"
+
+[dependencies]
+cranelift-entity = { path = "../../cranelift-entity", version = "0.30.0", default-features = false }
 
-[badges.travis-ci]
-repository = "CraneStation/cranelift"
+[badges]
+maintenance = { status = "experimental" }
+travis-ci = { repository = "CraneStation/cranelift" }
+
+[features]
+default = ["std"]
+std = ["cranelift-entity/std"]
+# The "core" feature enables a workaround for Cargo #4866.
+core = ["cranelift-entity/core"]
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/formats.rs
@@ -0,0 +1,246 @@
+use crate::cdsl::operands::{Operand, OperandKind};
+
+use std::collections::{HashMap, HashSet};
+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.
+    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.
+///
+/// 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.
+#[derive(Debug)]
+pub struct InstructionFormat {
+    pub name: &'static str,
+    pub num_value_operands: usize,
+    pub has_value_list: bool,
+    pub imm_fields: Vec<FormatField>,
+    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()
+            .map(|field| format!("{}: {}", field.member, field.kind.name))
+            .collect::<Vec<_>>()
+            .join(", ");
+        fmt.write_fmt(format_args!(
+            "{}(imms=({}), vals={})",
+            self.name, args, self.num_value_operands
+        ))?;
+        Ok(())
+    }
+}
+
+pub struct InstructionFormatBuilder {
+    name: &'static str,
+    num_value_operands: usize,
+    has_value_list: bool,
+    imm_fields: Vec<FormatField>,
+    typevar_operand: Option<usize>,
+}
+
+pub struct ImmParameter {
+    kind: OperandKind,
+    member: &'static str,
+}
+impl Into<ImmParameter> for (&'static str, &OperandKind) {
+    fn into(self) -> ImmParameter {
+        ImmParameter {
+            kind: self.1.clone(),
+            member: self.0,
+        }
+    }
+}
+impl Into<ImmParameter> for &OperandKind {
+    fn into(self) -> ImmParameter {
+        ImmParameter {
+            kind: self.clone(),
+            member: self.default_member.unwrap(),
+        }
+    }
+}
+
+impl InstructionFormatBuilder {
+    pub fn new(name: &'static str) -> Self {
+        Self {
+            name,
+            num_value_operands: 0,
+            has_value_list: false,
+            imm_fields: Vec::new(),
+            typevar_operand: None,
+        }
+    }
+
+    pub fn value(mut self) -> Self {
+        self.num_value_operands += 1;
+        self
+    }
+
+    pub fn varargs(mut self) -> Self {
+        self.has_value_list = true;
+        self
+    }
+
+    pub fn imm(mut self, param: impl Into<ImmParameter>) -> Self {
+        let imm_param = param.into();
+        let field = FormatField {
+            immnum: self.imm_fields.len(),
+            kind: imm_param.kind,
+            member: imm_param.member,
+        };
+        self.imm_fields.push(field);
+        self
+    }
+
+    pub fn typevar_operand(mut self, operand_index: usize) -> Self {
+        assert!(self.typevar_operand.is_none());
+        assert!(self.has_value_list || operand_index < self.num_value_operands);
+        self.typevar_operand = Some(operand_index);
+        self
+    }
+
+    pub fn finish(self) -> InstructionFormat {
+        let typevar_operand = if self.typevar_operand.is_some() {
+            self.typevar_operand
+        } else if self.has_value_list || self.num_value_operands > 0 {
+            // Default to the first value operand, if there's one.
+            Some(0)
+        } else {
+            None
+        };
+
+        InstructionFormat {
+            name: self.name,
+            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)]
+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>,
+    name_set: HashSet<&'static str>,
+}
+
+impl FormatRegistry {
+    pub fn new() -> Self {
+        Self {
+            sig_to_index: HashMap::new(),
+            map: PrimaryMap::new(),
+            name_set: HashSet::new(),
+        }
+    }
+
+    /// Find an existing instruction format that matches the given lists of instruction inputs and
+    /// outputs.
+    pub fn lookup(&self, operands_in: &Vec<Operand>) -> InstructionFormatIndex {
+        let mut imm_keys = Vec::new();
+        let mut num_values = 0;
+        let mut has_varargs = false;
+
+        for operand in operands_in.iter() {
+            if operand.is_value() {
+                num_values += 1;
+            }
+            has_varargs = has_varargs || operand.is_varargs();
+            if let Some(imm_key) = operand.kind.imm_key() {
+                imm_keys.push(imm_key);
+            }
+        }
+
+        let sig = (imm_keys, num_values, has_varargs);
+        *self
+            .sig_to_index
+            .get(&sig)
+            .expect("unknown InstructionFormat; please define it in shared/formats.rs first")
+    }
+
+    pub fn get(&self, index: InstructionFormatIndex) -> &InstructionFormat {
+        self.map.get(index).unwrap()
+    }
+
+    pub fn insert(&mut self, inst_format: InstructionFormatBuilder) {
+        let name = &inst_format.name;
+        if !self.name_set.insert(name) {
+            panic!(
+                "Trying to add an InstructionFormat named {}, but it already exists!",
+                name
+            );
+        }
+
+        let format = inst_format.finish();
+
+        // Compute key.
+        let imm_keys = format
+            .imm_fields
+            .iter()
+            .map(|field| field.kind.imm_key().unwrap())
+            .collect();
+        let key = (imm_keys, format.num_value_operands, format.has_value_list);
+
+        let index = self.map.push(format);
+        if let Some(already_inserted) = self.sig_to_index.insert(key, index) {
+            panic!(
+                "duplicate InstructionFormat: trying to insert '{}' while '{}' already has the same structure.",
+                self.map.get(index).unwrap().name,
+                self.map.get(already_inserted).unwrap().name
+            );
+        }
+    }
+
+    pub fn iter(&self) -> slice::Iter<InstructionFormat> {
+        self.map.values()
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/inst.rs
@@ -0,0 +1,458 @@
+use crate::cdsl::camel_case;
+use crate::cdsl::formats::{FormatRegistry, InstructionFormat, InstructionFormatIndex};
+use crate::cdsl::operands::Operand;
+use crate::cdsl::type_inference::Constraint;
+use crate::cdsl::typevar::TypeVar;
+
+use std::fmt;
+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,
+    instructions: Vec<Instruction>,
+}
+
+impl InstructionGroup {
+    pub fn new(name: &'static str, doc: &'static str) -> Self {
+        Self {
+            _name: name,
+            _doc: doc,
+            instructions: Vec::new(),
+        }
+    }
+
+    pub fn push(&mut self, inst: Instruction) {
+        self.instructions.push(inst);
+    }
+
+    pub fn iter(&self) -> slice::Iter<Instruction> {
+        self.instructions.iter()
+    }
+}
+
+pub struct PolymorphicInfo {
+    pub use_typevar_operand: bool,
+    pub ctrl_typevar: TypeVar,
+    pub other_typevars: Vec<TypeVar>,
+}
+
+pub struct Instruction {
+    /// 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>,
+
+    /// 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>,
+
+    pub value_opnums: Vec<usize>,
+    pub value_results: Vec<usize>,
+    pub imm_opnums: Vec<usize>,
+
+    /// True for instructions that terminate the EBB.
+    pub is_terminator: bool,
+    /// True for all branch or jump instructions.
+    pub is_branch: bool,
+    /// True for all indirect branch or jump instructions.',
+    pub is_indirect_branch: bool,
+    /// Is this a call instruction?
+    pub is_call: bool,
+    /// Is this a return instruction?
+    pub is_return: bool,
+    /// Is this a ghost instruction?
+    pub is_ghost: bool,
+    /// Can this instruction read from memory?
+    pub can_load: bool,
+    /// Can this instruction write to memory?
+    pub can_store: bool,
+    /// 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,
+}
+
+impl Instruction {
+    pub fn snake_name(&self) -> &'static str {
+        if self.name == "return" {
+            "return_"
+        } else {
+            self.name
+        }
+    }
+
+    pub fn doc_comment_first_line(&self) -> &'static str {
+        for line in self.doc.split("\n") {
+            let stripped = line.trim();
+            if stripped.len() > 0 {
+                return stripped;
+            }
+        }
+        ""
+    }
+}
+
+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()
+                .map(|op| op.name)
+                .collect::<Vec<_>>()
+                .join(", ");
+            fmt.write_str(&operands_out)?;
+            fmt.write_str(" = ")?;
+        }
+
+        fmt.write_str(self.name)?;
+
+        if self.operands_in.len() > 0 {
+            let operands_in = self
+                .operands_in
+                .iter()
+                .map(|op| op.name)
+                .collect::<Vec<_>>()
+                .join(", ");
+            fmt.write_str(" ")?;
+            fmt.write_str(&operands_in)?;
+        }
+
+        Ok(())
+    }
+}
+
+pub struct InstructionBuilder {
+    name: &'static str,
+    doc: &'static str,
+    operands_in: Option<Vec<Operand>>,
+    operands_out: Option<Vec<Operand>>,
+    constraints: Option<Vec<Constraint>>,
+
+    // See Instruction comments for the meaning of these fields.
+    is_terminator: bool,
+    is_branch: bool,
+    is_indirect_branch: bool,
+    is_call: bool,
+    is_return: bool,
+    is_ghost: bool,
+    can_load: bool,
+    can_store: bool,
+    can_trap: bool,
+    other_side_effects: bool,
+}
+
+impl InstructionBuilder {
+    pub fn new(name: &'static str, doc: &'static str) -> Self {
+        Self {
+            name,
+            doc,
+            operands_in: None,
+            operands_out: None,
+            constraints: None,
+
+            is_terminator: false,
+            is_branch: false,
+            is_indirect_branch: false,
+            is_call: false,
+            is_return: false,
+            is_ghost: false,
+            can_load: false,
+            can_store: false,
+            can_trap: false,
+            other_side_effects: false,
+        }
+    }
+
+    pub fn operands_in(mut self, operands: Vec<&Operand>) -> Self {
+        assert!(self.operands_in.is_none());
+        self.operands_in = Some(operands.iter().map(|x| (*x).clone()).collect());
+        self
+    }
+    pub fn operands_out(mut self, operands: Vec<&Operand>) -> Self {
+        assert!(self.operands_out.is_none());
+        self.operands_out = Some(operands.iter().map(|x| (*x).clone()).collect());
+        self
+    }
+    pub fn constraints(mut self, constraints: Vec<Constraint>) -> Self {
+        assert!(self.constraints.is_none());
+        self.constraints = Some(constraints);
+        self
+    }
+
+    pub fn is_terminator(mut self, val: bool) -> Self {
+        self.is_terminator = val;
+        self
+    }
+    pub fn is_branch(mut self, val: bool) -> Self {
+        self.is_branch = val;
+        self
+    }
+    pub fn is_indirect_branch(mut self, val: bool) -> Self {
+        self.is_indirect_branch = val;
+        self
+    }
+    pub fn is_call(mut self, val: bool) -> Self {
+        self.is_call = val;
+        self
+    }
+    pub fn is_return(mut self, val: bool) -> Self {
+        self.is_return = val;
+        self
+    }
+    pub fn is_ghost(mut self, val: bool) -> Self {
+        self.is_ghost = val;
+        self
+    }
+    pub fn can_load(mut self, val: bool) -> Self {
+        self.can_load = val;
+        self
+    }
+    pub fn can_store(mut self, val: bool) -> Self {
+        self.can_store = val;
+        self
+    }
+    pub fn can_trap(mut self, val: bool) -> Self {
+        self.can_trap = val;
+        self
+    }
+    pub fn other_side_effects(mut self, val: bool) -> Self {
+        self.other_side_effects = val;
+        self
+    }
+
+    pub fn finish(self, format_registry: &FormatRegistry) -> Instruction {
+        let operands_in = self.operands_in.unwrap_or_else(Vec::new);
+        let operands_out = self.operands_out.unwrap_or_else(Vec::new);
+
+        let format_index = format_registry.lookup(&operands_in);
+
+        let mut value_opnums = Vec::new();
+        let mut imm_opnums = Vec::new();
+        for (i, op) in operands_in.iter().enumerate() {
+            if op.is_value() {
+                value_opnums.push(i);
+            } else if op.is_immediate() {
+                imm_opnums.push(i);
+            } else {
+                assert!(op.is_varargs());
+            }
+        }
+
+        let mut value_results = Vec::new();
+        for (i, op) in operands_out.iter().enumerate() {
+            if op.is_value() {
+                value_results.push(i);
+            }
+        }
+
+        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,
+        }
+    }
+}
+
+/// 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.
+    let is_polymorphic = operands_in
+        .iter()
+        .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some())
+        || operands_out
+            .iter()
+            .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some());
+
+    if !is_polymorphic {
+        return None;
+    }
+
+    // Verify the use of type variables.
+    let mut use_typevar_operand = false;
+    let mut ctrl_typevar = None;
+    let mut other_typevars = None;
+    let mut maybe_error_message = None;
+
+    let tv_op = format.typevar_operand;
+    if let Some(tv_op) = tv_op {
+        if tv_op < value_opnums.len() {
+            let op_num = value_opnums[tv_op];
+            let tv = operands_in[op_num].type_var().unwrap();
+            let free_typevar = tv.free_typevar();
+            if (free_typevar.is_some() && tv == &free_typevar.unwrap())
+                || !tv.singleton_type().is_none()
+            {
+                match verify_ctrl_typevar(tv, &value_opnums, &operands_in, &operands_out) {
+                    Ok(typevars) => {
+                        other_typevars = Some(typevars);
+                        ctrl_typevar = Some(tv.clone());
+                        use_typevar_operand = true;
+                    }
+                    Err(error_message) => {
+                        maybe_error_message = Some(error_message);
+                    }
+                }
+            }
+        }
+    };
+
+    if !use_typevar_operand {
+        if operands_out.len() == 0 {
+            match maybe_error_message {
+                Some(msg) => panic!(msg),
+                None => panic!("typevar_operand must be a free type variable"),
+            }
+        }
+
+        let tv = operands_out[0].type_var().unwrap();
+        let free_typevar = tv.free_typevar();
+        if free_typevar.is_some() && tv != &free_typevar.unwrap() {
+            panic!("first result must be a free type variable");
+        }
+
+        other_typevars =
+            Some(verify_ctrl_typevar(tv, &value_opnums, &operands_in, &operands_out).unwrap());
+        ctrl_typevar = Some(tv.clone());
+    }
+
+    // rustc is not capable to determine this statically, so enforce it with options.
+    assert!(ctrl_typevar.is_some());
+    assert!(other_typevars.is_some());
+
+    Some(PolymorphicInfo {
+        use_typevar_operand,
+        ctrl_typevar: ctrl_typevar.unwrap(),
+        other_typevars: other_typevars.unwrap(),
+    })
+}
+
+/// Verify that the use of TypeVars is consistent with `ctrl_typevar` as the controlling type
+/// variable.
+///
+/// All polymorhic inputs must either be derived from `ctrl_typevar` or be independent free type
+/// variables only used once.
+///
+/// All polymorphic results must be derived from `ctrl_typevar`.
+///
+/// Return a vector of other type variables used, or panics.
+fn verify_ctrl_typevar(
+    ctrl_typevar: &TypeVar,
+    value_opnums: &Vec<usize>,
+    operands_in: &Vec<Operand>,
+    operands_out: &Vec<Operand>,
+) -> Result<Vec<TypeVar>, String> {
+    let mut other_typevars = Vec::new();
+
+    // Check value inputs.
+    for &op_num in value_opnums {
+        let typ = operands_in[op_num].type_var();
+
+        let tv = if let Some(typ) = typ {
+            typ.free_typevar()
+        } else {
+            None
+        };
+
+        // Non-polymorphic or derived from ctrl_typevar is OK.
+        let tv = match tv {
+            Some(tv) => {
+                if &tv == ctrl_typevar {
+                    continue;
+                }
+                tv
+            }
+            None => continue,
+        };
+
+        // No other derived typevars allowed.
+        if typ.is_some() && typ.unwrap() != &tv {
+            return Err(format!(
+                "{:?}: type variable {} must be derived from {:?}",
+                operands_in[op_num],
+                typ.unwrap().name,
+                ctrl_typevar
+            ));
+        }
+
+        // Other free type variables can only be used once each.
+        for other_tv in &other_typevars {
+            if &tv == other_tv {
+                return Err(format!(
+                    "type variable {} can't be used more than once",
+                    tv.name
+                ));
+            }
+        }
+
+        other_typevars.push(tv);
+    }
+
+    // Check outputs.
+    for result in operands_out {
+        if !result.is_value() {
+            continue;
+        }
+
+        let typ = result.type_var().unwrap();
+        let tv = typ.free_typevar();
+
+        // Non-polymorphic or derived form ctrl_typevar is OK.
+        if tv.is_none() || &tv.unwrap() == ctrl_typevar {
+            continue;
+        }
+
+        return Err("type variable in output not derived from ctrl_typevar".into());
+    }
+
+    Ok(other_typevars)
+}
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/isa.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/isa.rs
@@ -1,18 +1,26 @@
-use super::regs::IsaRegs;
-use super::settings::SettingGroup;
+use crate::cdsl::inst::InstructionGroup;
+use crate::cdsl::regs::IsaRegs;
+use crate::cdsl::settings::SettingGroup;
 
 pub struct TargetIsa {
     pub name: &'static str,
+    pub instructions: InstructionGroup,
     pub settings: SettingGroup,
     pub regs: IsaRegs,
 }
 
 impl TargetIsa {
-    pub fn new(name: &'static str, settings: SettingGroup, regs: IsaRegs) -> Self {
+    pub fn new(
+        name: &'static str,
+        instructions: InstructionGroup,
+        settings: SettingGroup,
+        regs: IsaRegs,
+    ) -> Self {
         Self {
             name,
+            instructions,
             settings,
             regs,
         }
     }
 }
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/mod.rs
@@ -1,17 +1,22 @@
 //! Cranelift DSL classes.
 //!
 //! This module defines the classes that are used to define Cranelift
 //! instructions and other entities.
 
+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;
 
 /// 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) => {
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/operands.rs
@@ -0,0 +1,285 @@
+use std::collections::HashMap;
+
+use crate::cdsl::camel_case;
+use crate::cdsl::typevar::TypeVar;
+
+/// An instruction operand can be an *immediate*, an *SSA value*, or an *entity reference*. The
+/// type of the operand is one of:
+///
+/// 1. A `ValueType` instance indicates an SSA value operand with a concrete type.
+///
+/// 2. A `TypeVar` instance indicates an SSA value operand, and the instruction is polymorphic over
+///    the possible concrete types that the type variable can assume.
+///
+/// 3. An `ImmediateKind` instance indicates an immediate operand whose value is encoded in the
+///    instruction itself rather than being passed as an SSA value.
+///
+/// 4. An `EntityRefKind` instance indicates an operand that references another entity in the
+///    function, typically something declared in the function preamble.
+#[derive(Clone, Debug)]
+pub struct Operand {
+    pub name: &'static str,
+    pub doc: Option<String>,
+    pub kind: OperandKind,
+}
+
+impl Operand {
+    pub fn is_value(&self) -> bool {
+        match self.kind.fields {
+            OperandKindFields::TypeVar(_) => true,
+            _ => false,
+        }
+    }
+
+    pub fn type_var(&self) -> Option<&TypeVar> {
+        match &self.kind.fields {
+            OperandKindFields::TypeVar(typevar) => Some(typevar),
+            _ => None,
+        }
+    }
+
+    pub fn is_varargs(&self) -> bool {
+        match self.kind.fields {
+            OperandKindFields::VariableArgs => true,
+            _ => false,
+        }
+    }
+
+    /// Returns true if the operand has an immediate kind or is an EntityRef.
+    // TODO inherited name from the python, rename to is_immediate_or_entityref later.
+    pub fn is_immediate(&self) -> bool {
+        match self.kind.fields {
+            OperandKindFields::ImmEnum(_)
+            | OperandKindFields::ImmValue
+            | OperandKindFields::EntityRef => true,
+            _ => false,
+        }
+    }
+
+    /// Returns true if the operand has an immediate kind.
+    pub fn is_pure_immediate(&self) -> bool {
+        match self.kind.fields {
+            OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => true,
+            _ => false,
+        }
+    }
+
+    pub fn is_cpu_flags(&self) -> bool {
+        match &self.kind.fields {
+            OperandKindFields::TypeVar(type_var)
+                if type_var.name == "iflags" || type_var.name == "fflags" =>
+            {
+                true
+            }
+            _ => false,
+        }
+    }
+}
+
+pub struct OperandBuilder {
+    name: &'static str,
+    doc: Option<String>,
+    kind: OperandKind,
+}
+
+impl OperandBuilder {
+    pub fn new(name: &'static str, kind: OperandKind) -> Self {
+        Self {
+            name,
+            doc: None,
+            kind,
+        }
+    }
+    pub fn doc(mut self, doc: impl Into<String>) -> Self {
+        assert!(self.doc.is_none());
+        self.doc = Some(doc.into());
+        self
+    }
+    pub fn finish(self) -> Operand {
+        let doc = match self.doc {
+            Some(doc) => Some(doc),
+            None => match &self.kind.fields {
+                OperandKindFields::TypeVar(tvar) => Some(tvar.doc.clone()),
+                _ => self.kind.doc.clone(),
+            },
+        };
+
+        Operand {
+            name: self.name,
+            doc,
+            kind: self.kind,
+        }
+    }
+}
+
+type EnumValues = HashMap<&'static str, &'static str>;
+
+#[derive(Clone, Debug)]
+pub enum OperandKindFields {
+    EntityRef,
+    VariableArgs,
+    ImmValue,
+    ImmEnum(EnumValues),
+    TypeVar(TypeVar),
+}
+
+#[derive(Clone, Debug)]
+pub struct OperandKind {
+    pub name: &'static str,
+
+    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,
+}
+
+impl OperandKind {
+    pub fn imm_key(&self) -> Option<String> {
+        match self.fields {
+            OperandKindFields::ImmEnum(_)
+            | OperandKindFields::ImmValue
+            | OperandKindFields::EntityRef => Some(self.name.to_string()),
+            _ => None,
+        }
+    }
+
+    pub fn type_var(&self) -> TypeVar {
+        match &self.fields {
+            OperandKindFields::TypeVar(tvar) => tvar.clone(),
+            _ => panic!("not a typevar"),
+        }
+    }
+}
+
+pub struct OperandKindBuilder {
+    name: &'static str,
+
+    doc: Option<String>,
+
+    default_member: Option<&'static str>,
+
+    /// The camel-cased name of an operand kind is also the Rust type used to represent it.
+    rust_type: Option<String>,
+
+    fields: OperandKindFields,
+}
+
+impl OperandKindBuilder {
+    pub fn new(name: &'static str, fields: OperandKindFields) -> Self {
+        Self {
+            name,
+            doc: None,
+            default_member: None,
+            rust_type: None,
+            fields,
+        }
+    }
+
+    pub fn new_imm(name: &'static str) -> Self {
+        Self {
+            name,
+            doc: None,
+            default_member: None,
+            rust_type: None,
+            fields: OperandKindFields::ImmValue,
+        }
+    }
+
+    pub fn new_enum(name: &'static str, values: EnumValues) -> Self {
+        Self {
+            name,
+            doc: None,
+            default_member: None,
+            rust_type: None,
+            fields: OperandKindFields::ImmEnum(values),
+        }
+    }
+
+    pub fn doc(mut self, doc: &'static str) -> Self {
+        assert!(self.doc.is_none());
+        self.doc = Some(doc.to_string());
+        self
+    }
+    pub fn default_member(mut self, default_member: &'static str) -> Self {
+        assert!(self.default_member.is_none());
+        self.default_member = Some(default_member);
+        self
+    }
+    pub fn rust_type(mut self, rust_type: &'static str) -> Self {
+        assert!(self.rust_type.is_none());
+        self.rust_type = Some(rust_type.to_string());
+        self
+    }
+
+    pub fn finish(self) -> OperandKind {
+        let default_member = match self.default_member {
+            Some(default_member) => Some(default_member),
+            None => match &self.fields {
+                OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => Some("imm"),
+                OperandKindFields::TypeVar(_) | OperandKindFields::EntityRef => Some(self.name),
+                OperandKindFields::VariableArgs => None,
+            },
+        };
+
+        let rust_type = match self.rust_type {
+            Some(rust_type) => rust_type.to_string(),
+            None => match &self.fields {
+                OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => {
+                    format!("ir::immediates::{}", camel_case(self.name))
+                }
+                OperandKindFields::VariableArgs => "&[Value]".to_string(),
+                OperandKindFields::TypeVar(_) | OperandKindFields::EntityRef => {
+                    format!("ir::{}", camel_case(self.name))
+                }
+            },
+        };
+
+        let doc = match self.doc {
+            Some(doc) => Some(doc),
+            None => match &self.fields {
+                OperandKindFields::TypeVar(type_var) => Some(type_var.doc.clone()),
+                OperandKindFields::ImmEnum(_)
+                | OperandKindFields::ImmValue
+                | OperandKindFields::EntityRef
+                | OperandKindFields::VariableArgs => None,
+            },
+        };
+
+        OperandKind {
+            name: self.name,
+            doc,
+            default_member,
+            rust_type,
+            fields: self.fields,
+        }
+    }
+}
+
+impl Into<OperandKind> for &TypeVar {
+    fn into(self) -> OperandKind {
+        OperandKindBuilder::new("value", OperandKindFields::TypeVar(self.into())).finish()
+    }
+}
+impl Into<OperandKind> for &OperandKind {
+    fn into(self) -> OperandKind {
+        self.clone()
+    }
+}
+
+/// Helper to create an operand in definitions files.
+pub fn create_operand(name: &'static str, kind: impl Into<OperandKind>) -> Operand {
+    OperandBuilder::new(name, kind.into()).finish()
+}
+
+/// Helper to create an operand with a documentation in definitions files.
+pub fn create_operand_doc(
+    name: &'static str,
+    kind: impl Into<OperandKind>,
+    doc: &'static str,
+) -> Operand {
+    OperandBuilder::new(name, kind.into()).doc(doc).finish()
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/type_inference.rs
@@ -0,0 +1,5 @@
+use crate::cdsl::typevar::TypeVar;
+
+pub enum Constraint {
+    WiderOrEq(TypeVar, TypeVar),
+}
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/types.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/types.rs
@@ -16,23 +16,23 @@ use crate::shared::types as shared_types
 //
 // Vector types are encoded with the lane type in the low 4 bits and log2(lanes)
 // in the high 4 bits, giving a range of 2-256 lanes.
 static LANE_BASE: u8 = 0x70;
 
 // Rust name prefix used for the `rust_name` method.
 static _RUST_NAME_PREFIX: &'static str = "ir::types::";
 
-// ValueType variants (i8, i32, ...) are provided in `base::types.rs`.
+// ValueType variants (i8, i32, ...) are provided in `shared::types.rs`.
 
 /// A concrete SSA value type.
 ///
 /// All SSA values have a type that is described by an instance of `ValueType`
 /// or one of its subclasses.
-#[derive(Debug)]
+#[derive(Clone, Debug, PartialEq)]
 pub enum ValueType {
     BV(BVType),
     Lane(LaneType),
     Special(SpecialType),
     Vector(VectorType),
 }
 
 impl ValueType {
@@ -85,17 +85,17 @@ impl ValueType {
             ValueType::BV(_) => None,
             ValueType::Lane(l) => Some(l.number()),
             ValueType::Special(s) => Some(s.number()),
             ValueType::Vector(ref v) => Some(v.number()),
         }
     }
 
     /// Return the name of this type for generated Rust source files.
-    pub fn _rust_name(&self) -> String {
+    pub fn rust_name(&self) -> String {
         format!("{}{}", _RUST_NAME_PREFIX, self.to_string().to_uppercase())
     }
 
     /// Return true iff:
     ///     1. self and other have equal number of lanes
     ///     2. each lane in self has at least as many bits as a lane in other
     pub fn _wider_or_equal(&self, rhs: &ValueType) -> bool {
         (self.lane_count() == rhs.lane_count()) && (self.lane_bits() >= rhs.lane_bits())
@@ -142,17 +142,17 @@ impl From<SpecialType> for ValueType {
 /// Create a ValueType from a given vector type.
 impl From<VectorType> for ValueType {
     fn from(vector: VectorType) -> Self {
         ValueType::Vector(vector)
     }
 }
 
 /// A concrete scalar type that can appear as a vector lane too.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq)]
 pub enum LaneType {
     BoolType(shared_types::Bool),
     FloatType(shared_types::Float),
     IntType(shared_types::Int),
 }
 
 impl LaneType {
     /// Return a string containing the documentation comment for this lane type.
@@ -200,16 +200,53 @@ impl LaneType {
                 LaneType::IntType(shared_types::Int::I8) => 5,
                 LaneType::IntType(shared_types::Int::I16) => 6,
                 LaneType::IntType(shared_types::Int::I32) => 7,
                 LaneType::IntType(shared_types::Int::I64) => 8,
                 LaneType::FloatType(shared_types::Float::F32) => 9,
                 LaneType::FloatType(shared_types::Float::F64) => 10,
             }
     }
+
+    pub fn bool_from_bits(num_bits: u16) -> LaneType {
+        LaneType::BoolType(match num_bits {
+            1 => shared_types::Bool::B1,
+            8 => shared_types::Bool::B8,
+            16 => shared_types::Bool::B16,
+            32 => shared_types::Bool::B32,
+            64 => shared_types::Bool::B64,
+            _ => unreachable!("unxpected num bits for bool"),
+        })
+    }
+
+    pub fn int_from_bits(num_bits: u16) -> LaneType {
+        LaneType::IntType(match num_bits {
+            8 => shared_types::Int::I8,
+            16 => shared_types::Int::I16,
+            32 => shared_types::Int::I32,
+            64 => shared_types::Int::I64,
+            _ => unreachable!("unxpected num bits for int"),
+        })
+    }
+
+    pub fn float_from_bits(num_bits: u16) -> LaneType {
+        LaneType::FloatType(match num_bits {
+            32 => shared_types::Float::F32,
+            64 => shared_types::Float::F64,
+            _ => unreachable!("unxpected num bits for float"),
+        })
+    }
+
+    pub fn by(&self, lanes: u16) -> ValueType {
+        if lanes == 1 {
+            (*self).into()
+        } else {
+            ValueType::Vector(VectorType::new(*self, lanes.into()))
+        }
+    }
 }
 
 impl fmt::Display for LaneType {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             LaneType::BoolType(_) => write!(f, "b{}", self.lane_bits()),
             LaneType::FloatType(_) => write!(f, "f{}", self.lane_bits()),
             LaneType::IntType(_) => write!(f, "i{}", self.lane_bits()),
@@ -285,16 +322,17 @@ impl Iterator for LaneTypeIterator {
         }
     }
 }
 
 /// A concrete SIMD vector type.
 ///
 /// A vector type has a lane type which is an instance of `LaneType`,
 /// and a positive number of lanes.
+#[derive(Clone, PartialEq)]
 pub struct VectorType {
     base: LaneType,
     lanes: u64,
 }
 
 impl VectorType {
     /// Initialize a new integer type with `n` bits.
     pub fn new(base: LaneType, lanes: u64) -> Self {
@@ -315,16 +353,21 @@ impl VectorType {
         self.base.lane_bits()
     }
 
     /// Return the number of lanes.
     pub fn lane_count(&self) -> u64 {
         self.lanes
     }
 
+    /// Return the lane type.
+    pub fn lane_type(&self) -> LaneType {
+        self.base
+    }
+
     /// Find the unique number associated with this vector type.
     ///
     /// Vector types are encoded with the lane type in the low 4 bits and
     /// log2(lanes) in the high 4 bits, giving a range of 2-256 lanes.
     pub fn number(&self) -> u8 {
         let lanes_log_2: u32 = 63 - self.lane_count().leading_zeros();
         let base_num = u32::from(self.base.number());
         let num = (lanes_log_2 << 4) + base_num;
@@ -345,24 +388,25 @@ impl fmt::Debug for VectorType {
             "VectorType(base={}, lanes={})",
             self.base,
             self.lane_count()
         )
     }
 }
 
 /// A flat bitvector type. Used for semantics description only.
+#[derive(Clone, PartialEq)]
 pub struct BVType {
     bits: u64,
 }
 
 impl BVType {
     /// Initialize a new bitvector type with `n` bits.
-    pub fn _new(bits: u64) -> Self {
-        Self { bits }
+    pub fn new(bits: u16) -> Self {
+        Self { bits: bits.into() }
     }
 
     /// Return a string containing the documentation comment for this bitvector type.
     pub fn doc(&self) -> String {
         format!("A bitvector type with {} bits.", self.bits)
     }
 
     /// Return the number of bits in a lane.
@@ -381,17 +425,17 @@ impl fmt::Debug for BVType {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "BVType(bits={})", self.lane_bits())
     }
 }
 
 /// A concrete scalar type that is neither a vector nor a lane type.
 ///
 /// Special types cannot be used to form vectors.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub enum SpecialType {
     Flag(shared_types::Flag),
 }
 
 impl SpecialType {
     /// Return a string containing the documentation comment for this special type.
     pub fn doc(self) -> String {
         match self {
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/typevar.rs
@@ -0,0 +1,901 @@
+use std::collections::BTreeSet;
+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;
+const MAX_BITVEC: u16 = MAX_BITS * MAX_LANES;
+
+/// Type variables can be used in place of concrete types when defining
+/// instructions. This makes the instructions *polymorphic*.
+///
+/// A type variable is restricted to vary over a subset of the value types.
+/// This subset is specified by a set of flags that control the permitted base
+/// types and whether the type variable can assume scalar or vector types, or
+/// both.
+#[derive(Debug)]
+pub struct TypeVarContent {
+    /// Short name of type variable used in instruction descriptions.
+    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>,
+
+    pub base: Option<TypeVarParent>,
+}
+
+#[derive(Clone, Debug)]
+pub struct TypeVar {
+    content: Rc<TypeVarContent>,
+}
+
+impl TypeVar {
+    pub fn new(name: impl Into<String>, doc: impl Into<String>, type_set: TypeSet) -> Self {
+        Self {
+            content: Rc::new(TypeVarContent {
+                name: name.into(),
+                doc: doc.into(),
+                type_set: Rc::new(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 {
+            ValueType::BV(bitvec_type) => {
+                let bits = bitvec_type.lane_bits() as RangeBound;
+                return TypeVar::new(name, doc, builder.bitvecs(bits..bits).finish());
+            }
+            ValueType::Special(special_type) => {
+                return TypeVar::new(name, doc, builder.specials(vec![special_type]).finish());
+            }
+            ValueType::Lane(lane_type) => (lane_type, 1),
+            ValueType::Vector(vec_type) => {
+                (vec_type.lane_type(), vec_type.lane_count() as RangeBound)
+            }
+        };
+
+        builder = builder.simd_lanes(num_lanes..num_lanes);
+
+        let builder = match scalar_type {
+            LaneType::IntType(int_type) => {
+                let bits = int_type as RangeBound;
+                builder.ints(bits..bits)
+            }
+            LaneType::FloatType(float_type) => {
+                let bits = float_type as RangeBound;
+                builder.floats(bits..bits)
+            }
+            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(),
+        }
+    }
+
+    /// 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
+    }
+
+    /// 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 {
+            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 {
+        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,
+                    "can't halve all integer types"
+                );
+                assert!(
+                    ts.floats.len() == 0 || *ts.floats.iter().min().unwrap() > 32,
+                    "can't halve all float types"
+                );
+                assert!(
+                    ts.bools.len() == 0 || *ts.bools.iter().min().unwrap() > 8,
+                    "can't halve all boolean types"
+                );
+            }
+            DerivedFunc::DoubleWidth => {
+                assert!(
+                    ts.ints.len() == 0 || *ts.ints.iter().max().unwrap() < MAX_BITS,
+                    "can't double all integer types"
+                );
+                assert!(
+                    ts.floats.len() == 0 || *ts.floats.iter().max().unwrap() < MAX_BITS,
+                    "can't double all float types"
+                );
+                assert!(
+                    ts.bools.len() == 0 || *ts.bools.iter().max().unwrap() < MAX_BITS,
+                    "can't double all boolean types"
+                );
+            }
+            DerivedFunc::HalfVector => {
+                assert!(
+                    *ts.lanes.iter().min().unwrap() > 1,
+                    "can't halve a scalar type"
+                );
+            }
+            DerivedFunc::DoubleVector => {
+                assert!(
+                    *ts.lanes.iter().max().unwrap() < MAX_LANES,
+                    "can't double 256 lanes"
+                );
+            }
+            DerivedFunc::LaneOf | DerivedFunc::ToBitVec | DerivedFunc::AsBool => {
+                /* no particular assertions */
+            }
+        }
+
+        return TypeVar {
+            content: Rc::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);
+    }
+    pub fn half_width(&self) -> TypeVar {
+        return self.derived(DerivedFunc::HalfWidth);
+    }
+    pub fn double_width(&self) -> TypeVar {
+        return self.derived(DerivedFunc::DoubleWidth);
+    }
+    pub fn half_vector(&self) -> 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);
+    }
+}
+
+impl Into<TypeVar> for &TypeVar {
+    fn into(self) -> TypeVar {
+        self.clone()
+    }
+}
+impl Into<TypeVar> for ValueType {
+    fn into(self) -> TypeVar {
+        TypeVar::new_singleton(self)
+    }
+}
+
+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),
+            (None, None) => Rc::ptr_eq(&self.content, &other.content),
+            _ => false,
+        }
+    }
+}
+
+impl ops::Deref for TypeVar {
+    type Target = TypeVarContent;
+    fn deref(&self) -> &Self::Target {
+        &*self.content
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum DerivedFunc {
+    LaneOf,
+    AsBool,
+    HalfWidth,
+    DoubleWidth,
+    HalfVector,
+    DoubleVector,
+    ToBitVec,
+}
+
+impl DerivedFunc {
+    pub fn name(&self) -> &'static str {
+        match self {
+            DerivedFunc::LaneOf => "lane_of",
+            DerivedFunc::AsBool => "as_bool",
+            DerivedFunc::HalfWidth => "half_width",
+            DerivedFunc::DoubleWidth => "double_width",
+            DerivedFunc::HalfVector => "half_vector",
+            DerivedFunc::DoubleVector => "double_vector",
+            DerivedFunc::ToBitVec => "to_bitvec",
+        }
+    }
+}
+
+#[derive(Debug)]
+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
+/// instead.
+///
+/// Objects of this class can be used as dictionary keys.
+///
+/// Parametrized type sets are specified in terms of ranges:
+/// - The permitted range of vector lanes, where 1 indicates a scalar type.
+/// - The permitted range of integer types.
+/// - The permitted range of floating point types, and
+/// - The permitted range of boolean types.
+///
+/// The ranges are inclusive from smallest bit-width to largest bit-width.
+///
+/// Finally, a type set can contain special types (derived from `SpecialType`)
+/// which can't appear as lane types.
+
+type RangeBound = u16;
+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)]
+pub struct TypeSet {
+    pub lanes: NumSet,
+    pub ints: NumSet,
+    pub floats: NumSet,
+    pub bools: NumSet,
+    pub bitvecs: NumSet,
+    pub specials: Vec<SpecialType>,
+}
+
+impl TypeSet {
+    fn new(
+        lanes: NumSet,
+        ints: NumSet,
+        floats: NumSet,
+        bools: NumSet,
+        bitvecs: NumSet,
+        specials: Vec<SpecialType>,
+    ) -> Self {
+        Self {
+            lanes,
+            ints,
+            floats,
+            bools,
+            bitvecs,
+            specials,
+        }
+    }
+
+    /// Return the number of concrete types represented by this typeset.
+    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 {
+            DerivedFunc::LaneOf => self.lane_of(),
+            DerivedFunc::AsBool => self.as_bool(),
+            DerivedFunc::HalfWidth => self.half_width(),
+            DerivedFunc::DoubleWidth => self.double_width(),
+            DerivedFunc::HalfVector => self.half_vector(),
+            DerivedFunc::DoubleVector => self.double_vector(),
+            DerivedFunc::ToBitVec => self.to_bitvec(),
+        }
+    }
+
+    /// Return a TypeSet describing the image of self across lane_of.
+    fn lane_of(&self) -> TypeSet {
+        let mut copy = self.clone();
+        copy.lanes = num_set![1];
+        copy.bitvecs = NumSet::new();
+        copy
+    }
+
+    /// Return a TypeSet describing the image of self across as_bool.
+    fn as_bool(&self) -> TypeSet {
+        let mut copy = self.clone();
+        copy.ints = NumSet::new();
+        copy.floats = NumSet::new();
+        copy.bitvecs = NumSet::new();
+        if (&self.lanes - &num_set![1]).len() > 0 {
+            copy.bools = &self.ints | &self.floats;
+            copy.bools = &copy.bools | &self.bools;
+        }
+        if self.lanes.contains(&1) {
+            copy.bools.insert(1);
+        }
+        copy
+    }
+
+    /// Return a TypeSet describing the image of self across halfwidth.
+    fn half_width(&self) -> TypeSet {
+        let mut copy = self.clone();
+        copy.ints = NumSet::from_iter(self.ints.iter().filter(|&&x| x > 8).map(|&x| x / 2));
+        copy.floats = NumSet::from_iter(self.floats.iter().filter(|&&x| x > 32).map(|&x| x / 2));
+        copy.bools = NumSet::from_iter(self.bools.iter().filter(|&&x| x > 8).map(|&x| x / 2));
+        copy.bitvecs = NumSet::from_iter(self.bitvecs.iter().filter(|&&x| x > 1).map(|&x| x / 2));
+        copy.specials = Vec::new();
+        copy
+    }
+
+    /// Return a TypeSet describing the image of self across doublewidth.
+    fn double_width(&self) -> TypeSet {
+        let mut copy = self.clone();
+        copy.ints = NumSet::from_iter(self.ints.iter().filter(|&&x| x < MAX_BITS).map(|&x| x * 2));
+        copy.floats = NumSet::from_iter(
+            self.floats
+                .iter()
+                .filter(|&&x| x < MAX_BITS)
+                .map(|&x| x * 2),
+        );
+        copy.bools = NumSet::from_iter(
+            self.bools
+                .iter()
+                .filter(|&&x| x < MAX_BITS)
+                .map(|&x| x * 2)
+                .filter(legal_bool),
+        );
+        copy.bitvecs = NumSet::from_iter(
+            self.bitvecs
+                .iter()
+                .filter(|&&x| x < MAX_BITVEC)
+                .map(|&x| x * 2),
+        );
+        copy.specials = Vec::new();
+        copy
+    }
+
+    /// Return a TypeSet describing the image of self across halfvector.
+    fn half_vector(&self) -> TypeSet {
+        let mut copy = self.clone();
+        copy.bitvecs = NumSet::new();
+        copy.lanes = NumSet::from_iter(self.lanes.iter().filter(|&&x| x > 1).map(|&x| x / 2));
+        copy.specials = Vec::new();
+        copy
+    }
+
+    /// Return a TypeSet describing the image of self across doublevector.
+    fn double_vector(&self) -> TypeSet {
+        let mut copy = self.clone();
+        copy.bitvecs = NumSet::new();
+        copy.lanes = NumSet::from_iter(
+            self.lanes
+                .iter()
+                .filter(|&&x| x < MAX_LANES)
+                .map(|&x| x * 2),
+        );
+        copy.specials = Vec::new();
+        copy
+    }
+
+    /// Return a TypeSet describing the image of self across to_bitvec.
+    fn to_bitvec(&self) -> TypeSet {
+        assert!(self.bitvecs.is_empty());
+        let all_scalars = &(&self.ints | &self.floats) | &self.bools;
+
+        let mut copy = self.clone();
+        copy.lanes = num_set![1];
+        copy.ints = NumSet::new();
+        copy.bools = NumSet::new();
+        copy.floats = NumSet::new();
+        copy.bitvecs = self
+            .lanes
+            .iter()
+            .cycle()
+            .zip(all_scalars.iter())
+            .map(|(num_lanes, lane_width)| num_lanes * lane_width)
+            .collect();
+
+        copy.specials = Vec::new();
+        copy
+    }
+
+    fn concrete_types(&self) -> Vec<ValueType> {
+        let mut ret = Vec::new();
+        for &num_lanes in &self.lanes {
+            for &bits in &self.ints {
+                ret.push(LaneType::int_from_bits(bits).by(num_lanes));
+            }
+            for &bits in &self.floats {
+                ret.push(LaneType::float_from_bits(bits).by(num_lanes));
+            }
+            for &bits in &self.bools {
+                ret.push(LaneType::bool_from_bits(bits).by(num_lanes));
+            }
+            for &bits in &self.bitvecs {
+                assert_eq!(num_lanes, 1);
+                ret.push(BVType::new(bits).into());
+            }
+        }
+        for &special in &self.specials {
+            ret.push(special.into());
+        }
+        ret
+    }
+
+    /// 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);
+    }
+}
+
+pub struct TypeSetBuilder {
+    ints: Interval,
+    floats: Interval,
+    bools: Interval,
+    bitvecs: Interval,
+    includes_scalars: bool,
+    simd_lanes: Interval,
+    specials: Vec<SpecialType>,
+}
+
+impl TypeSetBuilder {
+    pub fn new() -> Self {
+        Self {
+            ints: Interval::None,
+            floats: Interval::None,
+            bools: Interval::None,
+            bitvecs: Interval::None,
+            includes_scalars: true,
+            simd_lanes: Interval::None,
+            specials: Vec::new(),
+        }
+    }
+
+    pub fn ints(mut self, interval: impl Into<Interval>) -> Self {
+        assert!(self.ints == Interval::None);
+        self.ints = interval.into();
+        self
+    }
+    pub fn floats(mut self, interval: impl Into<Interval>) -> Self {
+        assert!(self.floats == Interval::None);
+        self.floats = interval.into();
+        self
+    }
+    pub fn bools(mut self, interval: impl Into<Interval>) -> Self {
+        assert!(self.bools == Interval::None);
+        self.bools = interval.into();
+        self
+    }
+    pub fn includes_scalars(mut self, includes_scalars: bool) -> Self {
+        self.includes_scalars = includes_scalars;
+        self
+    }
+    pub fn simd_lanes(mut self, interval: impl Into<Interval>) -> Self {
+        assert!(self.simd_lanes == Interval::None);
+        self.simd_lanes = interval.into();
+        self
+    }
+    pub fn bitvecs(mut self, interval: impl Into<Interval>) -> Self {
+        assert!(self.bitvecs == Interval::None);
+        self.bitvecs = interval.into();
+        self
+    }
+    pub fn specials(mut self, specials: Vec<SpecialType>) -> Self {
+        assert!(self.specials.is_empty());
+        self.specials = specials;
+        self
+    }
+
+    pub fn finish(self) -> TypeSet {
+        let min_lanes = if self.includes_scalars { 1 } else { 2 };
+;
+        let bools = range_to_set(self.bools.to_range(1..MAX_BITS, None))
+            .into_iter()
+            .filter(legal_bool)
+            .collect();
+
+        TypeSet::new(
+            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,
+        )
+    }
+}
+
+#[derive(PartialEq)]
+pub enum Interval {
+    None,
+    All,
+    Range(Range),
+}
+
+impl Interval {
+    fn to_range(&self, full_range: Range, default: Option<RangeBound>) -> Option<Range> {
+        match self {
+            Interval::None => {
+                if let Some(default_val) = default {
+                    Some(default_val..default_val)
+                } else {
+                    None
+                }
+            }
+
+            Interval::All => Some(full_range),
+
+            Interval::Range(range) => {
+                let (low, high) = (range.start, range.end);
+                assert!(low.is_power_of_two());
+                assert!(high.is_power_of_two());
+                assert!(low <= high);
+                assert!(low >= full_range.start);
+                assert!(high <= full_range.end);
+                Some(low..high)
+            }
+        }
+    }
+}
+
+impl Into<Interval> for Range {
+    fn into(self) -> Interval {
+        Interval::Range(self)
+    }
+}
+
+fn legal_bool(bits: &RangeBound) -> bool {
+    // Only allow legal bit widths for bool types.
+    *bits == 1 || (*bits >= 8 && *bits <= MAX_BITS && bits.is_power_of_two())
+}
+
+/// Generates a set with all the powers of two included in the range.
+fn range_to_set(range: Option<Range>) -> NumSet {
+    let mut set = NumSet::new();
+
+    let (low, high) = match range {
+        Some(range) => (range.start, range.end),
+        None => return set,
+    };
+
+    assert!(low.is_power_of_two());
+    assert!(high.is_power_of_two());
+    assert!(low <= high);
+
+    for i in low.trailing_zeros()..high.trailing_zeros() + 1 {
+        assert!(1 << i <= RangeBound::max_value());
+        set.insert(1 << i);
+    }
+    set
+}
+
+#[test]
+fn test_typevar_builder() {
+    let type_set = TypeSetBuilder::new().ints(Interval::All).finish();
+    assert_eq!(type_set.lanes, num_set![1]);
+    assert!(type_set.floats.is_empty());
+    assert_eq!(type_set.ints, num_set![8, 16, 32, 64]);
+    assert!(type_set.bools.is_empty());
+    assert!(type_set.bitvecs.is_empty());
+    assert!(type_set.specials.is_empty());
+
+    let type_set = TypeSetBuilder::new().bools(Interval::All).finish();
+    assert_eq!(type_set.lanes, num_set![1]);
+    assert!(type_set.floats.is_empty());
+    assert!(type_set.ints.is_empty());
+    assert_eq!(type_set.bools, num_set![1, 8, 16, 32, 64]);
+    assert!(type_set.bitvecs.is_empty());
+    assert!(type_set.specials.is_empty());
+
+    let type_set = TypeSetBuilder::new().floats(Interval::All).finish();
+    assert_eq!(type_set.lanes, num_set![1]);
+    assert_eq!(type_set.floats, num_set![32, 64]);
+    assert!(type_set.ints.is_empty());
+    assert!(type_set.bools.is_empty());
+    assert!(type_set.bitvecs.is_empty());
+    assert!(type_set.specials.is_empty());
+
+    let type_set = TypeSetBuilder::new()
+        .floats(Interval::All)
+        .simd_lanes(Interval::All)
+        .includes_scalars(false)
+        .finish();
+    assert_eq!(type_set.lanes, num_set![2, 4, 8, 16, 32, 64, 128, 256]);
+    assert_eq!(type_set.floats, num_set![32, 64]);
+    assert!(type_set.ints.is_empty());
+    assert!(type_set.bools.is_empty());
+    assert!(type_set.bitvecs.is_empty());
+    assert!(type_set.specials.is_empty());
+
+    let type_set = TypeSetBuilder::new()
+        .floats(Interval::All)
+        .simd_lanes(Interval::All)
+        .includes_scalars(true)
+        .finish();
+    assert_eq!(type_set.lanes, num_set![1, 2, 4, 8, 16, 32, 64, 128, 256]);
+    assert_eq!(type_set.floats, num_set![32, 64]);
+    assert!(type_set.ints.is_empty());
+    assert!(type_set.bools.is_empty());
+    assert!(type_set.bitvecs.is_empty());
+    assert!(type_set.specials.is_empty());
+
+    let type_set = TypeSetBuilder::new().ints(16..64).finish();
+    assert_eq!(type_set.lanes, num_set![1]);
+    assert_eq!(type_set.ints, num_set![16, 32, 64]);
+    assert!(type_set.floats.is_empty());
+    assert!(type_set.bools.is_empty());
+    assert!(type_set.bitvecs.is_empty());
+    assert!(type_set.specials.is_empty());
+}
+
+#[test]
+#[should_panic]
+fn test_typevar_builder_too_high_bound_panic() {
+    TypeSetBuilder::new().ints(16..2 * MAX_BITS).finish();
+}
+
+#[test]
+#[should_panic]
+fn test_typevar_builder_inverted_bounds_panic() {
+    TypeSetBuilder::new().ints(32..16).finish();
+}
+
+#[test]
+fn test_as_bool() {
+    let a = TypeSetBuilder::new()
+        .simd_lanes(2..8)
+        .ints(8..8)
+        .floats(32..32)
+        .finish();
+    assert_eq!(
+        a.lane_of(),
+        TypeSetBuilder::new().ints(8..8).floats(32..32).finish()
+    );
+
+    // Test as_bool with disjoint intervals.
+    let mut a_as_bool = TypeSetBuilder::new().simd_lanes(2..8).finish();
+    a_as_bool.bools = num_set![8, 32];
+    assert_eq!(a.as_bool(), a_as_bool);
+
+    let b = TypeSetBuilder::new()
+        .simd_lanes(1..8)
+        .ints(8..8)
+        .floats(32..32)
+        .finish();
+    let mut b_as_bool = TypeSetBuilder::new().simd_lanes(1..8).finish();
+    b_as_bool.bools = num_set![1, 8, 32];
+    assert_eq!(b.as_bool(), b_as_bool);
+}
+
+#[test]
+fn test_forward_images() {
+    let empty_set = TypeSetBuilder::new().finish();
+
+    // Half vector.
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(1..32)
+            .finish()
+            .half_vector(),
+        TypeSetBuilder::new().simd_lanes(1..16).finish()
+    );
+
+    // Double vector.
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(1..32)
+            .finish()
+            .double_vector(),
+        TypeSetBuilder::new().simd_lanes(2..64).finish()
+    );
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(128..256)
+            .finish()
+            .double_vector(),
+        TypeSetBuilder::new().simd_lanes(256..256).finish()
+    );
+
+    // Half width.
+    assert_eq!(
+        TypeSetBuilder::new().ints(8..32).finish().half_width(),
+        TypeSetBuilder::new().ints(8..16).finish()
+    );
+    assert_eq!(
+        TypeSetBuilder::new().floats(32..32).finish().half_width(),
+        empty_set
+    );
+    assert_eq!(
+        TypeSetBuilder::new().floats(32..64).finish().half_width(),
+        TypeSetBuilder::new().floats(32..32).finish()
+    );
+    assert_eq!(
+        TypeSetBuilder::new().bools(1..8).finish().half_width(),
+        empty_set
+    );
+    assert_eq!(
+        TypeSetBuilder::new().bools(1..32).finish().half_width(),
+        TypeSetBuilder::new().bools(8..16).finish()
+    );
+
+    // Double width.
+    assert_eq!(
+        TypeSetBuilder::new().ints(8..32).finish().double_width(),
+        TypeSetBuilder::new().ints(16..64).finish()
+    );
+    assert_eq!(
+        TypeSetBuilder::new().ints(32..64).finish().double_width(),
+        TypeSetBuilder::new().ints(64..64).finish()
+    );
+    assert_eq!(
+        TypeSetBuilder::new().floats(32..32).finish().double_width(),
+        TypeSetBuilder::new().floats(64..64).finish()
+    );
+    assert_eq!(
+        TypeSetBuilder::new().floats(32..64).finish().double_width(),
+        TypeSetBuilder::new().floats(64..64).finish()
+    );
+    assert_eq!(
+        TypeSetBuilder::new().bools(1..16).finish().double_width(),
+        TypeSetBuilder::new().bools(16..32).finish()
+    );
+    assert_eq!(
+        TypeSetBuilder::new().bools(32..64).finish().double_width(),
+        TypeSetBuilder::new().bools(64..64).finish()
+    );
+}
+
+#[test]
+#[should_panic]
+fn test_typeset_singleton_panic_nonsingleton_types() {
+    TypeSetBuilder::new()
+        .ints(8..8)
+        .floats(32..32)
+        .finish()
+        .get_singleton();
+}
+
+#[test]
+#[should_panic]
+fn test_typeset_singleton_panic_nonsingleton_lanes() {
+    TypeSetBuilder::new()
+        .simd_lanes(1..2)
+        .floats(32..32)
+        .finish()
+        .get_singleton();
+}
+
+#[test]
+fn test_typeset_singleton() {
+    use crate::shared::types as shared_types;
+    assert_eq!(
+        TypeSetBuilder::new().ints(16..16).finish().get_singleton(),
+        ValueType::Lane(shared_types::Int::I16.into())
+    );
+    assert_eq!(
+        TypeSetBuilder::new()
+            .floats(64..64)
+            .finish()
+            .get_singleton(),
+        ValueType::Lane(shared_types::Float::F64.into())
+    );
+    assert_eq!(
+        TypeSetBuilder::new().bools(1..1).finish().get_singleton(),
+        ValueType::Lane(shared_types::Bool::B1.into())
+    );
+    assert_eq!(
+        TypeSetBuilder::new()
+            .simd_lanes(4..4)
+            .ints(32..32)
+            .finish()
+            .get_singleton(),
+        LaneType::from(shared_types::Int::I32).by(4)
+    );
+}
+
+#[test]
+fn test_typevar_functions() {
+    let x = TypeVar::new(
+        "x",
+        "i16 and up",
+        TypeSetBuilder::new().ints(16..64).finish(),
+    );
+    assert_eq!(x.half_width().name, "half_width(x)");
+    assert_eq!(
+        x.half_width().double_width().name,
+        "double_width(half_width(x))"
+    );
+
+    let x = TypeVar::new("x", "up to i32", TypeSetBuilder::new().ints(8..32).finish());
+    assert_eq!(x.double_width().name, "double_width(x)");
+}
+
+#[test]
+fn test_typevar_singleton() {
+    use crate::cdsl::types::VectorType;
+    use crate::shared::types as shared_types;
+
+    // Test i32.
+    let typevar =
+        TypeVar::new_singleton(ValueType::Lane(LaneType::IntType(shared_types::Int::I32)));
+    assert_eq!(typevar.name, "i32");
+    assert_eq!(typevar.type_set.ints, num_set![32]);
+    assert!(typevar.type_set.floats.is_empty());
+    assert!(typevar.type_set.bools.is_empty());
+    assert!(typevar.type_set.bitvecs.is_empty());
+    assert!(typevar.type_set.specials.is_empty());
+    assert_eq!(typevar.type_set.lanes, num_set![1]);
+
+    // Test f32x4.
+    let typevar = TypeVar::new_singleton(ValueType::Vector(VectorType::new(
+        LaneType::FloatType(shared_types::Float::F32),
+        4,
+    )));
+    assert_eq!(typevar.name, "f32x4");
+    assert!(typevar.type_set.ints.is_empty());
+    assert_eq!(typevar.type_set.floats, num_set![32]);
+    assert_eq!(typevar.type_set.lanes, num_set![4]);
+    assert!(typevar.type_set.bools.is_empty());
+    assert!(typevar.type_set.bitvecs.is_empty());
+    assert!(typevar.type_set.specials.is_empty());
+}
--- a/third_party/rust/cranelift-codegen-meta/src/gen_registers.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/gen_registers.rs
@@ -127,14 +127,14 @@ fn gen_isa(isa: &TargetIsa, fmt: &mut Fo
         fmt.indent(|fmt| {
             fmtln!(fmt, "self as RegUnit");
         });
         fmtln!(fmt, "}");
     });
     fmtln!(fmt, "}");
 }
 
-pub fn generate(isa: &TargetIsa, base_filename: &str, out_dir: &str) -> Result<(), error::Error> {
+pub fn generate(isa: &TargetIsa, filename: &str, out_dir: &str) -> Result<(), error::Error> {
     let mut fmt = Formatter::new();
     gen_isa(&isa, &mut fmt);
-    fmt.update_file(format!("{}-{}.rs", base_filename, isa.name), out_dir)?;
+    fmt.update_file(filename, 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
@@ -1,21 +1,19 @@
 use crate::cdsl::camel_case;
-use crate::cdsl::isa::TargetIsa;
 use crate::cdsl::settings::{
     BoolSetting, Predicate, Preset, Setting, SettingGroup, SpecificSetting,
 };
 use crate::constant_hash::{generate_table, simple_hash};
 use crate::error;
-use crate::shared;
 use crate::srcgen::{Formatter, Match};
-use crate::unique_table::UniqueTable;
+use crate::unique_table::UniqueSeqTable;
 use std::collections::HashMap;
 
-enum ParentGroup {
+pub enum ParentGroup {
     None,
     Shared,
 }
 
 /// Emits the constructor of the Flags structure.
 fn gen_constructor(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) {
     let args = match parent {
         ParentGroup::None => "builder: Builder",
@@ -147,23 +145,19 @@ fn gen_getter(setting: &Setting, fmt: &m
             fmtln!(fmt, "}");
         }
         SpecificSetting::Enum(ref values) => {
             let ty = camel_case(setting.name);
             fmtln!(fmt, "pub fn {}(&self) -> {} {{", setting.name, ty);
             fmt.indent(|fmt| {
                 let mut m = Match::new(format!("self.bytes[{}]", setting.byte_offset));
                 for (i, v) in values.iter().enumerate() {
-                    m.arm(
-                        format!("{}", i),
-                        vec![],
-                        format!("{}::{}", ty, camel_case(v)),
-                    );
+                    m.arm_no_fields(format!("{}", i), format!("{}::{}", ty, camel_case(v)));
                 }
-                m.arm("_", vec![], "panic!(\"Invalid enum value\")");
+                m.arm_no_fields("_", "panic!(\"Invalid enum value\")");
                 fmt.add_match(m);
             });
             fmtln!(fmt, "}");
         }
         SpecificSetting::Num(_) => {
             fmtln!(fmt, "pub fn {}(&self) -> u8 {{", setting.name);
             fmt.indent(|fmt| {
                 fmtln!(fmt, "self.bytes[{}]", setting.byte_offset);
@@ -186,22 +180,22 @@ fn gen_pred_getter(predicate: &Predicate
 fn gen_getters(group: &SettingGroup, fmt: &mut Formatter) {
     fmt.doc_comment("User-defined settings.");
     fmtln!(fmt, "#[allow(dead_code)]");
     fmtln!(fmt, "impl Flags {");
     fmt.indent(|fmt| {
         fmt.doc_comment("Get a view of the boolean predicates.");
         fmtln!(
             fmt,
-            "pub fn predicate_view(&self) -> ::settings::PredicateView {"
+            "pub fn predicate_view(&self) -> crate::settings::PredicateView {"
         );
         fmt.indent(|fmt| {
             fmtln!(
                 fmt,
-                "::settings::PredicateView::new(&self.bytes[{}..])",
+                "crate::settings::PredicateView::new(&self.bytes[{}..])",
                 group.bool_start_byte_offset
             );
         });
         fmtln!(fmt, "}");
 
         if group.settings.len() > 0 {
             fmt.doc_comment("Dynamic numbered predicate getter.");
             fmtln!(fmt, "fn numbered_predicate(&self, p: usize) -> bool {");
@@ -237,17 +231,17 @@ impl<'a> SettingOrPreset<'a> {
             SettingOrPreset::Setting(s) => s.name,
             SettingOrPreset::Preset(p) => p.name,
         }
     }
 }
 
 /// Emits DESCRIPTORS, ENUMERATORS, HASH_TABLE and PRESETS.
 fn gen_descriptors(group: &SettingGroup, fmt: &mut Formatter) {
-    let mut enum_table: UniqueTable<&'static str> = UniqueTable::new();
+    let mut enum_table = UniqueSeqTable::new();
 
     let mut descriptor_index_map: HashMap<SettingOrPreset, usize> = HashMap::new();
 
     // Generate descriptors.
     fmtln!(
         fmt,
         "static DESCRIPTORS: [detail::Descriptor; {}] = [",
         group.settings.len() + group.presets.len()
@@ -440,22 +434,19 @@ fn gen_group(group: &SettingGroup, paren
     gen_constructor(group, parent, fmt);
     gen_enum_types(group, fmt);
     gen_getters(group, fmt);
     gen_descriptors(group, fmt);
     gen_template(group, fmt);
     gen_display(group, fmt);
 }
 
-pub fn generate_common(filename: &str, out_dir: &str) -> Result<SettingGroup, error::Error> {
-    let settings = shared::settings::generate();
+pub fn generate(
+    settings: &SettingGroup,
+    parent_group: ParentGroup,
+    filename: &str,
+    out_dir: &str,
+) -> Result<(), error::Error> {
     let mut fmt = Formatter::new();
-    gen_group(&settings, ParentGroup::None, &mut fmt);
+    gen_group(&settings, parent_group, &mut fmt);
     fmt.update_file(filename, out_dir)?;
-    Ok(settings)
-}
-
-pub fn generate(isa: &TargetIsa, prefix: &str, out_dir: &str) -> Result<(), error::Error> {
-    let mut fmt = Formatter::new();
-    gen_group(&isa.settings, ParentGroup::Shared, &mut fmt);
-    fmt.update_file(format!("{}-{}.rs", prefix, isa.name), out_dir)?;
     Ok(())
 }
--- 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,11 +1,13 @@
+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 {
     let mut regs = IsaRegsBuilder::new();
@@ -39,13 +41,16 @@ fn define_regs() -> IsaRegs {
     regs.add_class(builder);
 
     let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
     regs.add_class(builder);
 
     regs.finish()
 }
 
-pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
-    let settings = define_settings(shared_settings);
+pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
+    let settings = define_settings(&shared_defs.settings);
     let regs = define_regs();
-    TargetIsa::new("arm32", settings, regs)
+
+    let inst_group = InstructionGroup::new("arm32", "arm32 specific instruction set");
+
+    TargetIsa::new("arm32", inst_group, settings, regs)
 }
--- 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,11 +1,13 @@
+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 {
     let mut regs = IsaRegsBuilder::new();
@@ -35,13 +37,16 @@ fn define_registers() -> IsaRegs {
     regs.add_class(builder);
 
     let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
     regs.add_class(builder);
 
     regs.finish()
 }
 
-pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
-    let settings = define_settings(shared_settings);
+pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
+    let settings = define_settings(&shared_defs.settings);
     let regs = define_registers();
-    TargetIsa::new("arm64", settings, regs)
+
+    let inst_group = InstructionGroup::new("arm64", "arm64 specific instruction set");
+
+    TargetIsa::new("arm64", inst_group, settings, regs)
 }
--- a/third_party/rust/cranelift-codegen-meta/src/isa/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/isa/mod.rs
@@ -1,10 +1,10 @@
 use crate::cdsl::isa::TargetIsa;
-use crate::cdsl::settings::SettingGroup;
+use crate::shared::Definitions as SharedDefinitions;
 use std::fmt;
 
 mod arm32;
 mod arm64;
 mod riscv;
 mod x86;
 
 /// Represents known ISA target.
@@ -50,18 +50,18 @@ impl fmt::Display for Isa {
             Isa::Riscv => write!(f, "riscv"),
             Isa::X86 => write!(f, "x86"),
             Isa::Arm32 => write!(f, "arm32"),
             Isa::Arm64 => write!(f, "arm64"),
         }
     }
 }
 
-pub fn define(isas: &Vec<Isa>, shared_settings: &SettingGroup) -> Vec<TargetIsa> {
+pub fn define(isas: &Vec<Isa>, shared_defs: &mut SharedDefinitions) -> Vec<TargetIsa> {
     isas.iter()
         .map(|isa| match isa {
-            Isa::Riscv => riscv::define(shared_settings),
-            Isa::X86 => x86::define(shared_settings),
-            Isa::Arm32 => arm32::define(shared_settings),
-            Isa::Arm64 => arm64::define(shared_settings),
+            Isa::Riscv => riscv::define(shared_defs),
+            Isa::X86 => x86::define(shared_defs),
+            Isa::Arm32 => arm32::define(shared_defs),
+            Isa::Arm64 => arm64::define(shared_defs),
         })
         .collect()
 }
--- 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,11 +1,13 @@
+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::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)",
         false,
@@ -71,13 +73,16 @@ fn define_registers() -> IsaRegs {
     regs.add_class(builder);
 
     let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
     regs.add_class(builder);
 
     regs.finish()
 }
 
-pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
-    let settings = define_settings(shared_settings);
+pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
+    let settings = define_settings(&shared_defs.settings);
     let regs = define_registers();
-    TargetIsa::new("riscv", settings, regs)
+
+    let inst_group = InstructionGroup::new("riscv", "riscv specific instruction set");
+
+    TargetIsa::new("riscv", inst_group, settings, regs)
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/isa/x86/instructions.rs
@@ -0,0 +1,254 @@
+#![allow(non_snake_case)]
+
+use crate::cdsl::formats::FormatRegistry;
+use crate::cdsl::inst::{InstructionBuilder as Inst, InstructionGroup};
+use crate::cdsl::operands::{create_operand as operand, create_operand_doc as operand_doc};
+use crate::cdsl::types::ValueType;
+use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar};
+use crate::shared::types;
+
+pub fn define(format_registry: &FormatRegistry) -> InstructionGroup {
+    let mut ig = InstructionGroup::new("x86", "x86 specific instruction set");
+
+    let iflags: &TypeVar = &ValueType::Special(types::Flag::IFlags.into()).into();
+
+    let iWord = &TypeVar::new(
+        "iWord",
+        "A scalar integer machine word",
+        TypeSetBuilder::new().ints(32..64).finish(),
+    );
+    let nlo = &operand_doc("nlo", iWord, "Low part of numerator");
+    let nhi = &operand_doc("nhi", iWord, "High part of numerator");
+    let d = &operand_doc("d", iWord, "Denominator");
+    let q = &operand_doc("q", iWord, "Quotient");
+    let r = &operand_doc("r", iWord, "Remainder");
+
+    ig.push(
+        Inst::new(
+            "x86_udivmodx",
+            r#"
+        Extended unsigned division.
+
+        Concatenate the bits in `nhi` and `nlo` to form the numerator.
+        Interpret the bits as an unsigned number and divide by the unsigned
+        denominator `d`. Trap when `d` is zero or if the quotient is larger
+        than the range of the output.
+
+        Return both quotient and remainder.
+        "#,
+        )
+        .operands_in(vec![nlo, nhi, d])
+        .operands_out(vec![q, r])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "x86_sdivmodx",
+            r#"
+        Extended signed division.
+
+        Concatenate the bits in `nhi` and `nlo` to form the numerator.
+        Interpret the bits as a signed number and divide by the signed
+        denominator `d`. Trap when `d` is zero or if the quotient is outside
+        the range of the output.
+
+        Return both quotient and remainder.
+        "#,
+        )
+        .operands_in(vec![nlo, nhi, d])
+        .operands_out(vec![q, r])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    let argL = &operand("argL", iWord);
+    let argR = &operand("argR", iWord);
+    let resLo = &operand("resLo", iWord);
+    let resHi = &operand("resHi", iWord);
+
+    ig.push(
+        Inst::new(
+            "x86_umulx",
+            r#"
+        Unsigned integer multiplication, producing a double-length result.
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![argL, argR])
+        .operands_out(vec![resLo, resHi])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "x86_smulx",
+            r#"
+        Signed integer multiplication, producing a double-length result.
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![argL, argR])
+        .operands_out(vec![resLo, resHi])
+        .finish(format_registry),
+    );
+
+    let Float = &TypeVar::new(
+        "Float",
+        "A scalar or vector floating point number",
+        TypeSetBuilder::new()
+            .floats(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+    let IntTo = &TypeVar::new(
+        "IntTo",
+        "An integer type with the same number of lanes",
+        TypeSetBuilder::new()
+            .ints(32..64)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+    let x = &operand("x", Float);
+    let a = &operand("a", IntTo);
+
+    ig.push(
+        Inst::new(
+            "x86_cvtt2si",
+            r#"
+        Convert with truncation floating point to signed integer.
+
+        The source floating point operand is converted to a signed integer by
+        rounding towards zero. If the result can't be represented in the output
+        type, returns the smallest signed value the output type can represent.
+
+        This instruction does not trap.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", Float);
+    let a = &operand("a", Float);
+    let y = &operand("y", Float);
+
+    ig.push(
+        Inst::new(
+            "x86_fmin",
+            r#"
+        Floating point minimum with x86 semantics.
+
+        This is equivalent to the C ternary operator `x < y ? x : y` which
+        differs from :inst:`fmin` when either operand is NaN or when comparing
+        +0.0 to -0.0.
+
+        When the two operands don't compare as LT, `y` is returned unchanged,
+        even if it is a signalling NaN.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "x86_fmax",
+            r#"
+        Floating point maximum with x86 semantics.
+
+        This is equivalent to the C ternary operator `x > y ? x : y` which
+        differs from :inst:`fmax` when either operand is NaN or when comparing
+        +0.0 to -0.0.
+
+        When the two operands don't compare as GT, `y` is returned unchanged,
+        even if it is a signalling NaN.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", iWord);
+
+    ig.push(
+        Inst::new(
+            "x86_push",
+            r#"
+    Pushes a value onto the stack.
+
+    Decrements the stack pointer and stores the specified value on to the top.
+
+    This is polymorphic in i32 and i64. However, it is only implemented for i64
+    in 64-bit mode, and only for i32 in 32-bit mode.
+    "#,
+        )
+        .operands_in(vec![x])
+        .other_side_effects(true)
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "x86_pop",
+            r#"
+    Pops a value from the stack.
+
+    Loads a value from the top of the stack and then increments the stack
+    pointer.
+
+    This is polymorphic in i32 and i64. However, it is only implemented for i64
+    in 64-bit mode, and only for i32 in 32-bit mode.
+    "#,
+        )
+        .operands_out(vec![x])
+        .other_side_effects(true)
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    let y = &operand("y", iWord);
+    let rflags = &operand("rflags", iflags);
+
+    ig.push(
+        Inst::new(
+            "x86_bsr",
+            r#"
+    Bit Scan Reverse -- returns the bit-index of the most significant 1
+    in the word. Result is undefined if the argument is zero. However, it
+    sets the Z flag depending on the argument, so it is at least easy to
+    detect and handle that case.
+
+    This is polymorphic in i32 and i64. It is implemented for both i64 and
+    i32 in 64-bit mode, and only for i32 in 32-bit mode.
+    "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![y, rflags])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "x86_bsf",
+            r#"
+    Bit Scan Forwards -- returns the bit-index of the least significant 1
+    in the word. Is otherwise identical to 'bsr', just above.
+    "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![y, rflags])
+        .finish(format_registry),
+    );
+
+    ig
+}
--- 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,12 +1,16 @@
+mod instructions;
+
 use crate::cdsl::isa::TargetIsa;
 use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
 use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
 
+use crate::shared::Definitions as SharedDefinitions;
+
 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);
@@ -104,13 +108,16 @@ fn define_registers() -> IsaRegs {
     regs.add_class(builder);
 
     let builder = RegClassBuilder::subclass_of("FPR8", fpr, 0, 8);
     regs.add_class(builder);
 
     regs.finish()
 }
 
-pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
-    let settings = define_settings(shared_settings);
+pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
+    let settings = define_settings(&shared_defs.settings);
     let regs = define_registers();
-    TargetIsa::new("x86", settings, regs)
+
+    let inst_group = instructions::define(&shared_defs.format_registry);
+
+    TargetIsa::new("x86", inst_group, settings, regs)
 }
--- a/third_party/rust/cranelift-codegen-meta/src/lib.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/lib.rs
@@ -15,22 +15,33 @@ mod unique_table;
 
 pub fn isa_from_arch(arch: &str) -> Result<isa::Isa, String> {
     isa::Isa::from_arch(arch).ok_or_else(|| format!("no supported isa found for arch `{}`", arch))
 }
 
 /// 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 shared_settings = gen_settings::generate_common("new_settings.rs", &out_dir)?;
+    let mut shared_defs = shared::define();
 
+    gen_settings::generate(
+        &shared_defs.settings,
+        gen_settings::ParentGroup::None,
+        "new_settings.rs",
+        &out_dir,
+    )?;
     gen_types::generate("types.rs", &out_dir)?;
 
     // Per ISA definitions.
-    let isas = isa::define(isas, &shared_settings);
+    let isas = isa::define(isas, &mut shared_defs);
 
     for isa in isas {
-        gen_registers::generate(&isa, "registers", &out_dir)?;
-        gen_settings::generate(&isa, "new_settings", &out_dir)?;
+        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),
+            &out_dir,
+        )?;
     }
 
     Ok(())
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/shared/entities.rs
@@ -0,0 +1,65 @@
+use crate::cdsl::operands::{OperandKind, OperandKindBuilder as Builder, OperandKindFields};
+
+/// Small helper to initialize an OperandBuilder with the right kind, for a given name and doc.
+fn create(name: &'static str, doc: &'static str) -> Builder {
+    Builder::new(name, OperandKindFields::EntityRef).doc(doc)
+}
+
+pub fn define() -> Vec<OperandKind> {
+    let mut kinds = Vec::new();
+
+    // A reference to an extended basic block in the same function.
+    // This is primarliy used in control flow instructions.
+    let ebb = create("ebb", "An extended basic block in the same function.")
+        .default_member("destination")
+        .finish();
+    kinds.push(ebb);
+
+    // A reference to a stack slot declared in the function preamble.
+    let stack_slot = create("stack_slot", "A stack slot").finish();
+    kinds.push(stack_slot);
+
+    // A reference to a global value.
+    let global_value = create("global_value", "A global value.").finish();
+    kinds.push(global_value);
+
+    // A reference to a function signature declared in the function preamble.
+    // This is used to provide the call signature in a call_indirect instruction.
+    let sig_ref = create("sig_ref", "A function signature.").finish();
+    kinds.push(sig_ref);
+
+    // A reference to an external function declared in the function preamble.
+    // This is used to provide the callee and signature in a call instruction.
+    let func_ref = create("func_ref", "An external function.").finish();
+    kinds.push(func_ref);
+
+    // A reference to a jump table declared in the function preamble.
+    let jump_table = create("jump_table", "A jump table.")
+        .default_member("table")
+        .finish();
+    kinds.push(jump_table);
+
+    // A reference to a heap declared in the function preamble.
+    let heap = create("heap", "A heap.").finish();
+    kinds.push(heap);
+
+    // A reference to a table declared in the function preamble.
+    let table = create("table", "A table.").finish();
+    kinds.push(table);
+
+    // A variable-sized list of value operands. Use for Ebb and function call arguments.
+    let varargs = Builder::new("variable_args", OperandKindFields::VariableArgs)
+        .doc(
+            r#"
+            A variable size list of `value` operands.
+
+            Use this to represent arguments passed to a function call, arguments
+            passed to an extended basic block, or a variable number of results
+            returned from an instruction.
+        "#,
+        )
+        .finish();
+    kinds.push(varargs);
+
+    return kinds;
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/shared/formats.rs
@@ -0,0 +1,184 @@
+use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder as Builder};
+use crate::shared::OperandKinds;
+
+pub fn define(immediates: &OperandKinds, entities: &OperandKinds) -> FormatRegistry {
+    // Shorthands for immediates.
+    let uimm8 = immediates.by_name("uimm8");
+    let uimm32 = immediates.by_name("uimm32");
+    let imm64 = immediates.by_name("imm64");
+    let ieee32 = immediates.by_name("ieee32");
+    let ieee64 = immediates.by_name("ieee64");
+    let boolean = immediates.by_name("boolean");
+    let intcc = immediates.by_name("intcc");
+    let floatcc = immediates.by_name("floatcc");
+    let memflags = immediates.by_name("memflags");
+    let offset32 = immediates.by_name("offset32");
+    let trapcode = immediates.by_name("trapcode");
+    let regunit = immediates.by_name("regunit");
+
+    // Shorthands for entities.
+    let global_value = entities.by_name("global_value");
+    let ebb = entities.by_name("ebb");
+    let jump_table = entities.by_name("jump_table");
+    let func_ref = entities.by_name("func_ref");
+    let sig_ref = entities.by_name("sig_ref");
+    let stack_slot = entities.by_name("stack_slot");
+    let heap = entities.by_name("heap");
+    let table = entities.by_name("table");
+
+    let mut registry = FormatRegistry::new();
+
+    registry.insert(Builder::new("Unary").value());
+    registry.insert(Builder::new("UnaryImm").imm(imm64));
+    registry.insert(Builder::new("UnaryIeee32").imm(ieee32));
+    registry.insert(Builder::new("UnaryIeee64").imm(ieee64));
+    registry.insert(Builder::new("UnaryBool").imm(boolean));
+    registry.insert(Builder::new("UnaryGlobalValue").imm(global_value));
+
+    registry.insert(Builder::new("Binary").value().value());
+    registry.insert(Builder::new("BinaryImm").value().imm(imm64));
+
+    // The select instructions are controlled by the second VALUE operand.
+    // The first VALUE operand is the controlling flag which has a derived type.
+    // The fma instruction has the same constraint on all inputs.
+    registry.insert(
+        Builder::new("Ternary")
+            .value()
+            .value()
+            .value()
+            .typevar_operand(1),
+    );
+
+    // Catch-all for instructions with many outputs and inputs and no immediate
+    // operands.
+    registry.insert(Builder::new("MultiAry").varargs());
+
+    registry.insert(Builder::new("NullAry"));
+
+    registry.insert(
+        Builder::new("InsertLane")
+            .value()
+            .imm(("lane", uimm8))
+            .value(),
+    );
+    registry.insert(Builder::new("ExtractLane").value().imm(("lane", uimm8)));
+
+    registry.insert(Builder::new("IntCompare").imm(intcc).value().value());
+    registry.insert(Builder::new("IntCompareImm").imm(intcc).value().imm(imm64));
+    registry.insert(Builder::new("IntCond").imm(intcc).value());
+
+    registry.insert(Builder::new("FloatCompare").imm(floatcc).value().value());
+    registry.insert(Builder::new("FloatCond").imm(floatcc).value());;
+
+    registry.insert(Builder::new("IntSelect").imm(intcc).value().value().value());
+
+    registry.insert(Builder::new("Jump").imm(ebb).varargs());
+    registry.insert(Builder::new("Branch").value().imm(ebb).varargs());
+    registry.insert(
+        Builder::new("BranchInt")
+            .imm(intcc)
+            .value()
+            .imm(ebb)
+            .varargs(),
+    );
+    registry.insert(
+        Builder::new("BranchFloat")
+            .imm(floatcc)
+            .value()
+            .imm(ebb)
+            .varargs(),
+    );
+    registry.insert(
+        Builder::new("BranchIcmp")
+            .imm(intcc)
+            .value()
+            .value()
+            .imm(ebb)
+            .varargs(),
+    );
+    registry.insert(Builder::new("BranchTable").value().imm(ebb).imm(jump_table));
+    registry.insert(
+        Builder::new("BranchTableEntry")
+            .value()
+            .value()
+            .imm(uimm8)
+            .imm(jump_table),
+    );
+    registry.insert(Builder::new("BranchTableBase").imm(jump_table));
+    registry.insert(Builder::new("IndirectJump").value().imm(jump_table));
+
+    registry.insert(Builder::new("Call").imm(func_ref).varargs());
+    registry.insert(Builder::new("CallIndirect").imm(sig_ref).value().varargs());
+    registry.insert(Builder::new("FuncAddr").imm(func_ref));
+
+    registry.insert(Builder::new("Load").imm(memflags).value().imm(offset32));
+    registry.insert(
+        Builder::new("LoadComplex")
+            .imm(memflags)
+            .varargs()
+            .imm(offset32),
+    );
+    registry.insert(
+        Builder::new("Store")
+            .imm(memflags)
+            .value()
+            .value()
+            .imm(offset32),
+    );
+    registry.insert(
+        Builder::new("StoreComplex")
+            .imm(memflags)
+            .value()
+            .varargs()
+            .imm(offset32),
+    );
+    registry.insert(Builder::new("StackLoad").imm(stack_slot).imm(offset32));
+    registry.insert(
+        Builder::new("StackStore")
+            .value()
+            .imm(stack_slot)
+            .imm(offset32),
+    );
+
+    // Accessing a WebAssembly heap.
+    registry.insert(Builder::new("HeapAddr").imm(heap).value().imm(uimm32));
+
+    // Accessing a WebAssembly table.
+    registry.insert(Builder::new("TableAddr").imm(table).value().imm(offset32));
+
+    registry.insert(
+        Builder::new("RegMove")
+            .value()
+            .imm(("src", regunit))
+            .imm(("dst", regunit)),
+    );
+    registry.insert(
+        Builder::new("CopySpecial")
+            .imm(("src", regunit))
+            .imm(("dst", regunit)),
+    );
+    registry.insert(
+        Builder::new("RegSpill")
+            .value()
+            .imm(("src", regunit))
+            .imm(("dst", stack_slot)),
+    );
+    registry.insert(
+        Builder::new("RegFill")
+            .value()
+            .imm(("src", stack_slot))
+            .imm(("dst", regunit)),
+    );
+
+    registry.insert(Builder::new("Trap").imm(trapcode));
+    registry.insert(Builder::new("CondTrap").value().imm(trapcode));
+    registry.insert(Builder::new("IntCondTrap").imm(intcc).value().imm(trapcode));
+    registry.insert(
+        Builder::new("FloatCondTrap")
+            .imm(floatcc)
+            .value()
+            .imm(trapcode),
+    );
+
+    registry
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/shared/immediates.rs
@@ -0,0 +1,145 @@
+use crate::cdsl::operands::{OperandKind, OperandKindBuilder as Builder};
+
+use std::collections::HashMap;
+
+pub fn define() -> Vec<OperandKind> {
+    let mut kinds = Vec::new();
+
+    // A 64-bit immediate integer operand.
+    //
+    // This type of immediate integer can interact with SSA values with any
+    // IntType type.
+    let imm64 = Builder::new_imm("imm64")
+        .doc("A 64-bit immediate integer.")
+        .finish();
+    kinds.push(imm64);
+
+    // An unsigned 8-bit immediate integer operand.
+    //
+    // This small operand is used to indicate lane indexes in SIMD vectors and
+    // immediate bit counts on shift instructions.
+    let uimm8 = Builder::new_imm("uimm8")
+        .doc("An 8-bit immediate unsigned integer.")
+        .finish();
+    kinds.push(uimm8);
+
+    // An unsigned 32-bit immediate integer operand.
+    let uimm32 = Builder::new_imm("uimm32")
+        .doc("A 32-bit immediate unsigned integer.")
+        .finish();
+    kinds.push(uimm32);
+
+    // A 32-bit immediate signed offset.
+    //
+    // This is used to represent an immediate address offset in load/store
+    // instructions.
+    let offset32 = Builder::new_imm("offset32")
+        .doc("A 32-bit immediate signed offset.")
+        .default_member("offset")
+        .finish();
+    kinds.push(offset32);
+
+    // A 32-bit immediate floating point operand.
+    //
+    // IEEE 754-2008 binary32 interchange format.
+    let ieee32 = Builder::new_imm("ieee32")
+        .doc("A 32-bit immediate floating point number.")
+        .finish();
+    kinds.push(ieee32);
+
+    // A 64-bit immediate floating point operand.
+    //
+    // IEEE 754-2008 binary64 interchange format.
+    let ieee64 = Builder::new_imm("ieee64")
+        .doc("A 64-bit immediate floating point number.")
+        .finish();
+    kinds.push(ieee64);
+
+    // An immediate boolean operand.
+    //
+    // This type of immediate boolean can interact with SSA values with any
+    // BoolType type.
+    let boolean = Builder::new_imm("boolean")
+        .doc("An immediate boolean.")
+        .rust_type("bool")
+        .finish();
+    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("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")
+        .finish();
+    kinds.push(intcc);
+
+    // A condition code for comparing floating point values.  This enumerated operand kind is used
+    // for the `fcmp` instruction and corresponds to the `condcodes::FloatCC` Rust type.
+    let mut floatcc_values = HashMap::new();
+    floatcc_values.insert("ord", "Ordered");
+    floatcc_values.insert("uno", "Unordered");
+    floatcc_values.insert("eq", "Equal");
+    floatcc_values.insert("ne", "NotEqual");
+    floatcc_values.insert("one", "OrderedNotEqual");
+    floatcc_values.insert("ueq", "UnorderedOrEqual");
+    floatcc_values.insert("lt", "LessThan");
+    floatcc_values.insert("le", "LessThanOrEqual");
+    floatcc_values.insert("gt", "GreaterThan");
+    floatcc_values.insert("ge", "GreaterThanOrEqual");
+    floatcc_values.insert("ult", "UnorderedOrLessThan");
+    floatcc_values.insert("ule", "UnorderedOrLessThanOrEqual");
+    floatcc_values.insert("ugt", "UnorderedOrGreaterThan");
+    floatcc_values.insert("uge", "UnorderedOrGreaterThanOrEqual");
+    let floatcc = Builder::new_enum("floatcc", floatcc_values)
+        .doc("A floating point comparison condition code")
+        .default_member("cond")
+        .rust_type("ir::condcodes::FloatCC")
+        .finish();
+    kinds.push(floatcc);
+
+    // Flags for memory operations like :clif:inst:`load` and :clif:inst:`store`.
+    let memflags = Builder::new_imm("memflags")
+        .doc("Memory operation flags")
+        .default_member("flags")
+        .rust_type("ir::MemFlags")
+        .finish();
+    kinds.push(memflags);
+
+    // A register unit in the current target ISA.
+    let regunit = Builder::new_imm("regunit")
+        .doc("A register unit in the target ISA")
+        .rust_type("isa::RegUnit")
+        .finish();
+    kinds.push(regunit);
+
+    // A trap code indicating the reason for trapping.
+    //
+    // The Rust enum type also has a `User(u16)` variant for user-provided trap
+    // codes.
+    let mut trapcode_values = HashMap::new();
+    trapcode_values.insert("stk_ovf", "StackOverflow");
+    trapcode_values.insert("heap_oob", "HeapOutOfBounds");
+    trapcode_values.insert("int_ovf", "IntegerOverflow");
+    trapcode_values.insert("int_divz", "IntegerDivisionByZero");
+    let trapcode = Builder::new_enum("trapcode", trapcode_values)
+        .doc("A trap reason code.")
+        .default_member("code")
+        .rust_type("ir::TrapCode")
+        .finish();
+    kinds.push(trapcode);
+
+    return kinds;
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/shared/instructions.rs
@@ -0,0 +1,3185 @@
+#![allow(non_snake_case)]
+
+use crate::cdsl::formats::FormatRegistry;
+use crate::cdsl::inst::{InstructionBuilder as Inst, InstructionGroup};
+use crate::cdsl::operands::{create_operand as operand, create_operand_doc as operand_doc};
+use crate::cdsl::type_inference::Constraint::WiderOrEq;
+use crate::cdsl::types::{LaneType, ValueType};
+use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar};
+use crate::shared::{types, OperandKinds};
+
+pub fn define(
+    format_registry: &FormatRegistry,
+    immediates: &OperandKinds,
+    entities: &OperandKinds,
+) -> InstructionGroup {
+    let mut ig = InstructionGroup::new("base", "Shared base instruction set");
+
+    // Operand kind shorthands.
+    let intcc = immediates.by_name("intcc");
+    let floatcc = immediates.by_name("floatcc");
+    let trapcode = immediates.by_name("trapcode");
+    let uimm8 = immediates.by_name("uimm8");
+    let uimm32 = immediates.by_name("uimm32");
+    let imm64 = immediates.by_name("imm64");
+    let offset32 = immediates.by_name("offset32");
+    let memflags = immediates.by_name("memflags");
+    let ieee32 = immediates.by_name("ieee32");
+    let ieee64 = immediates.by_name("ieee64");
+    let boolean = immediates.by_name("boolean");
+    let regunit = immediates.by_name("regunit");
+
+    let ebb = entities.by_name("ebb");
+    let jump_table = entities.by_name("jump_table");
+    let variable_args = entities.by_name("variable_args");
+    let func_ref = entities.by_name("func_ref");
+    let sig_ref = entities.by_name("sig_ref");
+    let stack_slot = entities.by_name("stack_slot");
+    let global_value = entities.by_name("global_value");
+    let heap = entities.by_name("heap");
+    let table = entities.by_name("table");
+
+    let iflags: &TypeVar = &ValueType::Special(types::Flag::IFlags.into()).into();
+    let fflags: &TypeVar = &ValueType::Special(types::Flag::FFlags.into()).into();
+
+    let b1: &TypeVar = &ValueType::from(LaneType::from(types::Bool::B1)).into();
+    let f32_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F32)).into();
+    let f64_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F64)).into();
+
+    // Starting definitions.
+    let Int = &TypeVar::new(
+        "Int",
+        "A scalar or vector integer type",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+
+    let Bool = &TypeVar::new(
+        "Bool",
+        "A scalar or vector boolean type",
+        TypeSetBuilder::new()
+            .bools(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+
+    let iB = &TypeVar::new(
+        "iB",
+        "A scalar integer type",
+        TypeSetBuilder::new().ints(Interval::All).finish(),
+    );
+
+    let iAddr = &TypeVar::new(
+        "iAddr",
+        "An integer address type",
+        TypeSetBuilder::new().ints(32..64).finish(),
+    );
+
+    let Testable = &TypeVar::new(
+        "Testable",
+        "A scalar boolean or integer type",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .bools(Interval::All)
+            .finish(),
+    );
+
+    let TxN = &TypeVar::new(
+        "TxN",
+        "A SIMD vector type",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .floats(Interval::All)
+            .bools(Interval::All)
+            .simd_lanes(Interval::All)
+            .includes_scalars(false)
+            .finish(),
+    );
+
+    let Any = &TypeVar::new(
+        "Any",
+        "Any integer, float, or boolean scalar or vector type",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .floats(Interval::All)
+            .bools(Interval::All)
+            .simd_lanes(Interval::All)
+            .includes_scalars(true)
+            .finish(),
+    );
+
+    let Mem = &TypeVar::new(
+        "Mem",
+        "Any type that can be stored in memory",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .floats(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+
+    let MemTo = &TypeVar::new(
+        "MemTo",
+        "Any type that can be stored in memory",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .floats(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+
+    let addr = &operand("addr", iAddr);
+    let c = &operand_doc("c", Testable, "Controlling value to test");
+    let Cond = &operand("Cond", intcc);
+    let x = &operand("x", iB);
+    let y = &operand("y", iB);
+    let EBB = &operand_doc("EBB", ebb, "Destination extended basic block");
+    let args = &operand_doc("args", variable_args, "EBB arguments");
+
+    ig.push(
+        Inst::new(
+            "jump",
+            r#"
+        Jump.
+
+        Unconditionally jump to an extended basic block, passing the specified
+        EBB arguments. The number and types of arguments must match the
+        destination EBB.
+        "#,
+        )
+        .operands_in(vec![EBB, args])
+        .is_terminator(true)
+        .is_branch(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fallthrough",
+            r#"
+        Fall through to the next EBB.
+
+        This is the same as :inst:`jump`, except the destination EBB must be
+        the next one in the layout.
+
+        Jumps are turned into fall-through instructions by the branch
+        relaxation pass. There is no reason to use this instruction outside
+        that pass.
+        "#,
+        )
+        .operands_in(vec![EBB, args])
+        .is_terminator(true)
+        .is_branch(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "brz",
+            r#"
+        Branch when zero.
+
+        If ``c`` is a :type:`b1` value, take the branch when ``c`` is false. If
+        ``c`` is an integer value, take the branch when ``c = 0``.
+        "#,
+        )
+        .operands_in(vec![c, EBB, args])
+        .is_branch(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "brnz",
+            r#"
+        Branch when non-zero.
+
+        If ``c`` is a :type:`b1` value, take the branch when ``c`` is true. If
+        ``c`` is an integer value, take the branch when ``c != 0``.
+        "#,
+        )
+        .operands_in(vec![c, EBB, args])
+        .is_branch(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "br_icmp",
+            r#"
+        Compare scalar integers and branch.
+
+        Compare ``x`` and ``y`` in the same way as the :inst:`icmp` instruction
+        and take the branch if the condition is true::
+
+            br_icmp ugt v1, v2, ebb4(v5, v6)
+
+        is semantically equivalent to::
+
+            v10 = icmp ugt, v1, v2
+            brnz v10, ebb4(v5, v6)
+
+        Some RISC architectures like MIPS and RISC-V provide instructions that
+        implement all or some of the condition codes. The instruction can also
+        be used to represent *macro-op fusion* on architectures like Intel's.
+        "#,
+        )
+        .operands_in(vec![Cond, x, y, EBB, args])
+        .is_branch(true)
+        .finish(format_registry),
+    );
+
+    let f = &operand("f", iflags);
+
+    ig.push(
+        Inst::new(
+            "brif",
+            r#"
+        Branch when condition is true in integer CPU flags.
+        "#,
+        )
+        .operands_in(vec![Cond, f, EBB, args])
+        .is_branch(true)
+        .finish(format_registry),
+    );
+
+    let Cond = &operand("Cond", floatcc);
+    let f = &operand("f", fflags);
+
+    ig.push(
+        Inst::new(
+            "brff",
+            r#"
+        Branch when condition is true in floating point CPU flags.
+        "#,
+        )
+        .operands_in(vec![Cond, f, EBB, args])
+        .is_branch(true)
+        .finish(format_registry),
+    );
+
+    let x = &operand_doc("x", iB, "index into jump table");
+
+    let Entry = &TypeVar::new(
+        "Entry",
+        "A scalar integer type",
+        TypeSetBuilder::new().ints(Interval::All).finish(),
+    );
+
+    let entry = &operand_doc("entry", Entry, "entry of jump table");
+    let JT = &operand("JT", jump_table);
+
+    ig.push(
+        Inst::new(
+            "br_table",
+            r#"
+        Indirect branch via jump table.
+
+        Use ``x`` as an unsigned index into the jump table ``JT``. If a jump
+        table entry is found, branch to the corresponding EBB. If no entry was
+        found or the index is out-of-bounds, branch to the given default EBB.
+
+        Note that this branch instruction can't pass arguments to the targeted
+        blocks. Split critical edges as needed to work around this.
+
+        Do not confuse this with "tables" in WebAssembly. ``br_table`` is for
+        jump tables with destinations within the current function only -- think
+        of a ``match`` in Rust or a ``switch`` in C.  If you want to call a
+        function in a dynamic library, that will typically use
+        ``call_indirect``.
+        "#,
+        )
+        .operands_in(vec![x, EBB, JT])
+        .is_terminator(true)
+        .is_branch(true)
+        .finish(format_registry),
+    );
+
+    let Size = &operand_doc("Size", uimm8, "Size in bytes");
+
+    ig.push(
+        Inst::new(
+            "jump_table_entry",
+            r#"
+    Get an entry from a jump table.
+
+    Load a serialized ``entry`` from a jump table ``JT`` at a given index
+    ``addr`` with a specific ``Size``. The retrieved entry may need to be
+    decoded after loading, depending upon the jump table type used.
+
+    Currently, the only type supported is entries which are relative to the
+    base of the jump table.
+    "#,
+        )
+        .operands_in(vec![x, addr, Size, JT])
+        .operands_out(vec![entry])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "jump_table_base",
+            r#"
+    Get the absolute base address of a jump table.
+
+    This is used for jump tables wherein the entries are stored relative to
+    the base of jump table. In order to use these, generated code should first
+    load an entry using ``jump_table_entry``, then use this instruction to add
+    the relative base back to it.
+    "#,
+        )
+        .operands_in(vec![JT])
+        .operands_out(vec![addr])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "indirect_jump_table_br",
+            r#"
+    Branch indirectly via a jump table entry.
+
+    Unconditionally jump via a jump table entry that was previously loaded
+    with the ``jump_table_entry`` instruction.
+    "#,
+        )
+        .operands_in(vec![addr, JT])
+        .is_indirect_branch(true)
+        .is_terminator(true)
+        .is_branch(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "debugtrap",
+            r#"
+    Encodes an assembly debug trap.
+    "#,
+        )
+        .other_side_effects(true)
+        .can_load(true)
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    let code = &operand("code", trapcode);
+
+    ig.push(
+        Inst::new(
+            "trap",
+            r#"
+        Terminate execution unconditionally.
+        "#,
+        )
+        .operands_in(vec![code])
+        .can_trap(true)
+        .is_terminator(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "trapz",
+            r#"
+        Trap when zero.
+
+        if ``c`` is non-zero, execution continues at the following instruction.
+        "#,
+        )
+        .operands_in(vec![c, code])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "trapnz",
+            r#"
+        Trap when non-zero.
+
+        if ``c`` is zero, execution continues at the following instruction.
+        "#,
+        )
+        .operands_in(vec![c, code])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    let Cond = &operand("Cond", intcc);
+    let f = &operand("f", iflags);
+
+    ig.push(
+        Inst::new(
+            "trapif",
+            r#"
+        Trap when condition is true in integer CPU flags.
+        "#,
+        )
+        .operands_in(vec![Cond, f, code])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    let Cond = &operand("Cond", floatcc);
+    let f = &operand("f", fflags);
+
+    ig.push(
+        Inst::new(
+            "trapff",
+            r#"
+        Trap when condition is true in floating point CPU flags.
+        "#,
+        )
+        .operands_in(vec![Cond, f, code])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    let rvals = &operand_doc("rvals", variable_args, "return values");
+
+    ig.push(
+        Inst::new(
+            "return",
+            r#"
+        Return from the function.
+
+        Unconditionally transfer control to the calling function, passing the
+        provided return values. The list of return values must match the
+        function signature's return types.
+        "#,
+        )
+        .operands_in(vec![rvals])
+        .is_return(true)
+        .is_terminator(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fallthrough_return",
+            r#"
+        Return from the function by fallthrough.
+
+        This is a specialized instruction for use where one wants to append
+        a custom epilogue, which will then perform the real return. This
+        instruction has no encoding.
+        "#,
+        )
+        .operands_in(vec![rvals])
+        .is_return(true)
+        .is_terminator(true)
+        .finish(format_registry),
+    );
+
+    let FN = &operand_doc(
+        "FN",
+        func_ref,
+        "function to call, declared by :inst:`function`",
+    );
+    let args = &operand_doc("args", variable_args, "call arguments");
+
+    ig.push(
+        Inst::new(
+            "call",
+            r#"
+        Direct function call.
+
+        Call a function which has been declared in the preamble. The argument
+        types must match the function's signature.
+        "#,
+        )
+        .operands_in(vec![FN, args])
+        .operands_out(vec![rvals])
+        .is_call(true)
+        .finish(format_registry),
+    );
+
+    let SIG = &operand_doc("SIG", sig_ref, "function signature");
+    let callee = &operand_doc("callee", iAddr, "address of function to call");
+
+    ig.push(
+        Inst::new(
+            "call_indirect",
+            r#"
+        Indirect function call.
+
+        Call the function pointed to by `callee` with the given arguments. The
+        called function must match the specified signature.
+
+        Note that this is different from WebAssembly's ``call_indirect``; the
+        callee is a native address, rather than a table index. For WebAssembly,
+        :inst:`table_addr` and :inst:`load` are used to obtain a native address
+        from a table.
+        "#,
+        )
+        .operands_in(vec![SIG, callee, args])
+        .operands_out(vec![rvals])
+        .is_call(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "func_addr",
+            r#"
+        Get the address of a function.
+
+        Compute the absolute address of a function declared in the preamble.
+        The returned address can be used as a ``callee`` argument to
+        :inst:`call_indirect`. This is also a method for calling functions that
+        are too far away to be addressable by a direct :inst:`call`
+        instruction.
+        "#,
+        )
+        .operands_in(vec![FN])
+        .operands_out(vec![addr])
+        .finish(format_registry),
+    );
+
+    let SS = &operand("SS", stack_slot);
+    let Offset = &operand_doc("Offset", offset32, "Byte offset from base address");
+    let x = &operand_doc("x", Mem, "Value to be stored");
+    let a = &operand_doc("a", Mem, "Value loaded");
+    let p = &operand("p", iAddr);
+    let MemFlags = &operand("MemFlags", memflags);
+    let args = &operand_doc("args", variable_args, "Address arguments");
+
+    ig.push(
+        Inst::new(
+            "load",
+            r#"
+        Load from memory at ``p + Offset``.
+
+        This is a polymorphic instruction that can load any value type which
+        has a memory representation.
+        "#,
+        )
+        .operands_in(vec![MemFlags, p, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "load_complex",
+            r#"
+        Load from memory at ``sum(args) + Offset``.
+
+        This is a polymorphic instruction that can load any value type which
+        has a memory representation.
+        "#,
+        )
+        .operands_in(vec![MemFlags, args, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "store",
+            r#"
+        Store ``x`` to memory at ``p + Offset``.
+
+        This is a polymorphic instruction that can store any value type with a
+        memory representation.
+        "#,
+        )
+        .operands_in(vec![MemFlags, x, p, Offset])
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "store_complex",
+            r#"
+        Store ``x`` to memory at ``sum(args) + Offset``.
+
+        This is a polymorphic instruction that can store any value type with a
+        memory representation.
+        "#,
+        )
+        .operands_in(vec![MemFlags, x, args, Offset])
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    let iExt8 = &TypeVar::new(
+        "iExt8",
+        "An integer type with more than 8 bits",
+        TypeSetBuilder::new().ints(16..64).finish(),
+    );
+    let x = &operand("x", iExt8);
+    let a = &operand("a", iExt8);
+
+    ig.push(
+        Inst::new(
+            "uload8",
+            r#"
+        Load 8 bits from memory at ``p + Offset`` and zero-extend.
+
+        This is equivalent to ``load.i8`` followed by ``uextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, p, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "uload8_complex",
+            r#"
+        Load 8 bits from memory at ``sum(args) + Offset`` and zero-extend.
+
+        This is equivalent to ``load.i8`` followed by ``uextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, args, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sload8",
+            r#"
+        Load 8 bits from memory at ``p + Offset`` and sign-extend.
+
+        This is equivalent to ``load.i8`` followed by ``sextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, p, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sload8_complex",
+            r#"
+        Load 8 bits from memory at ``sum(args) + Offset`` and sign-extend.
+
+        This is equivalent to ``load.i8`` followed by ``sextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, args, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "istore8",
+            r#"
+        Store the low 8 bits of ``x`` to memory at ``p + Offset``.
+
+        This is equivalent to ``ireduce.i8`` followed by ``store.i8``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, x, p, Offset])
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "istore8_complex",
+            r#"
+        Store the low 8 bits of ``x`` to memory at ``sum(args) + Offset``.
+
+        This is equivalent to ``ireduce.i8`` followed by ``store.i8``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, x, args, Offset])
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    let iExt16 = &TypeVar::new(
+        "iExt16",
+        "An integer type with more than 16 bits",
+        TypeSetBuilder::new().ints(32..64).finish(),
+    );
+    let x = &operand("x", iExt16);
+    let a = &operand("a", iExt16);
+
+    ig.push(
+        Inst::new(
+            "uload16",
+            r#"
+        Load 16 bits from memory at ``p + Offset`` and zero-extend.
+
+        This is equivalent to ``load.i16`` followed by ``uextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, p, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "uload16_complex",
+            r#"
+        Load 16 bits from memory at ``sum(args) + Offset`` and zero-extend.
+
+        This is equivalent to ``load.i16`` followed by ``uextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, args, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sload16",
+            r#"
+        Load 16 bits from memory at ``p + Offset`` and sign-extend.
+
+        This is equivalent to ``load.i16`` followed by ``sextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, p, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sload16_complex",
+            r#"
+        Load 16 bits from memory at ``sum(args) + Offset`` and sign-extend.
+
+        This is equivalent to ``load.i16`` followed by ``sextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, args, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "istore16",
+            r#"
+        Store the low 16 bits of ``x`` to memory at ``p + Offset``.
+
+        This is equivalent to ``ireduce.i16`` followed by ``store.i16``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, x, p, Offset])
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "istore16_complex",
+            r#"
+        Store the low 16 bits of ``x`` to memory at ``sum(args) + Offset``.
+
+        This is equivalent to ``ireduce.i16`` followed by ``store.i16``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, x, args, Offset])
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    let iExt32 = &TypeVar::new(
+        "iExt32",
+        "An integer type with more than 32 bits",
+        TypeSetBuilder::new().ints(64..64).finish(),
+    );
+    let x = &operand("x", iExt32);
+    let a = &operand("a", iExt32);
+
+    ig.push(
+        Inst::new(
+            "uload32",
+            r#"
+        Load 32 bits from memory at ``p + Offset`` and zero-extend.
+
+        This is equivalent to ``load.i32`` followed by ``uextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, p, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "uload32_complex",
+            r#"
+        Load 32 bits from memory at ``sum(args) + Offset`` and zero-extend.
+
+        This is equivalent to ``load.i32`` followed by ``uextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, args, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sload32",
+            r#"
+        Load 32 bits from memory at ``p + Offset`` and sign-extend.
+
+        This is equivalent to ``load.i32`` followed by ``sextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, p, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sload32_complex",
+            r#"
+        Load 32 bits from memory at ``sum(args) + Offset`` and sign-extend.
+
+        This is equivalent to ``load.i32`` followed by ``sextend``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, args, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "istore32",
+            r#"
+        Store the low 32 bits of ``x`` to memory at ``p + Offset``.
+
+        This is equivalent to ``ireduce.i32`` followed by ``store.i32``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, x, p, Offset])
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "istore32_complex",
+            r#"
+        Store the low 32 bits of ``x`` to memory at ``sum(args) + Offset``.
+
+        This is equivalent to ``ireduce.i32`` followed by ``store.i32``.
+        "#,
+        )
+        .operands_in(vec![MemFlags, x, args, Offset])
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    let x = &operand_doc("x", Mem, "Value to be stored");
+    let a = &operand_doc("a", Mem, "Value loaded");
+    let Offset = &operand_doc("Offset", offset32, "In-bounds offset into stack slot");
+
+    ig.push(
+        Inst::new(
+            "stack_load",
+            r#"
+        Load a value from a stack slot at the constant offset.
+
+        This is a polymorphic instruction that can load any value type which
+        has a memory representation.
+
+        The offset is an immediate constant, not an SSA value. The memory
+        access cannot go out of bounds, i.e.
+        :math:`sizeof(a) + Offset <= sizeof(SS)`.
+        "#,
+        )
+        .operands_in(vec![SS, Offset])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "stack_store",
+            r#"
+        Store a value to a stack slot at a constant offset.
+
+        This is a polymorphic instruction that can store any value type with a
+        memory representation.
+
+        The offset is an immediate constant, not an SSA value. The memory
+        access cannot go out of bounds, i.e.
+        :math:`sizeof(a) + Offset <= sizeof(SS)`.
+        "#,
+        )
+        .operands_in(vec![x, SS, Offset])
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "stack_addr",
+            r#"
+        Get the address of a stack slot.
+
+        Compute the absolute address of a byte in a stack slot. The offset must
+        refer to a byte inside the stack slot:
+        :math:`0 <= Offset < sizeof(SS)`.
+        "#,
+        )
+        .operands_in(vec![SS, Offset])
+        .operands_out(vec![addr])
+        .finish(format_registry),
+    );
+
+    let GV = &operand("GV", global_value);
+
+    ig.push(
+        Inst::new(
+            "global_value",
+            r#"
+        Compute the value of global GV.
+        "#,
+        )
+        .operands_in(vec![GV])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "symbol_value",
+            r#"
+        Compute the value of global GV, which is a symbolic value.
+        "#,
+        )
+        .operands_in(vec![GV])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let HeapOffset = &TypeVar::new(
+        "HeapOffset",
+        "An unsigned heap offset",
+        TypeSetBuilder::new().ints(32..64).finish(),
+    );
+
+    let H = &operand("H", heap);
+    let p = &operand("p", HeapOffset);
+    let Size = &operand_doc("Size", uimm32, "Size in bytes");
+
+    ig.push(
+        Inst::new(
+            "heap_addr",
+            r#"
+        Bounds check and compute absolute address of heap memory.
+
+        Verify that the offset range ``p .. p + Size - 1`` is in bounds for the
+        heap H, and generate an absolute address that is safe to dereference.
+
+        1. If ``p + Size`` is not greater than the heap bound, return an
+           absolute address corresponding to a byte offset of ``p`` from the
+           heap's base address.
+        2. If ``p + Size`` is greater than the heap bound, generate a trap.
+        "#,
+        )
+        .operands_in(vec![H, p, Size])
+        .operands_out(vec![addr])
+        .finish(format_registry),
+    );
+
+    let TableOffset = &TypeVar::new(
+        "TableOffset",
+        "An unsigned table offset",
+        TypeSetBuilder::new().ints(32..64).finish(),
+    );
+    let T = &operand("T", table);
+    let p = &operand("p", TableOffset);
+    let Offset = &operand_doc("Offset", offset32, "Byte offset from element address");
+
+    ig.push(
+        Inst::new(
+            "table_addr",
+            r#"
+        Bounds check and compute absolute address of a table entry.
+
+        Verify that the offset ``p`` is in bounds for the table T, and generate
+        an absolute address that is safe to dereference.
+
+        ``Offset`` must be less than the size of a table element.
+
+        1. If ``p`` is not greater than the table bound, return an absolute
+           address corresponding to a byte offset of ``p`` from the table's
+           base address.
+        2. If ``p`` is greater than the table bound, generate a trap.
+        "#,
+        )
+        .operands_in(vec![T, p, Offset])
+        .operands_out(vec![addr])
+        .finish(format_registry),
+    );
+
+    let N = &operand("N", imm64);
+    let a = &operand_doc("a", Int, "A constant integer scalar or vector value");
+
+    ig.push(
+        Inst::new(
+            "iconst",
+            r#"
+        Integer constant.
+
+        Create a scalar integer SSA value with an immediate constant value, or
+        an integer vector where all the lanes have the same value.
+        "#,
+        )
+        .operands_in(vec![N])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let N = &operand("N", ieee32);
+    let a = &operand_doc("a", f32_, "A constant f32 scalar value");
+
+    ig.push(
+        Inst::new(
+            "f32const",
+            r#"
+        Floating point constant.
+
+        Create a :type:`f32` SSA value with an immediate constant value.
+        "#,
+        )
+        .operands_in(vec![N])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let N = &operand("N", ieee64);
+    let a = &operand_doc("a", f64_, "A constant f64 scalar value");
+
+    ig.push(
+        Inst::new(
+            "f64const",
+            r#"
+        Floating point constant.
+
+        Create a :type:`f64` SSA value with an immediate constant value.
+        "#,
+        )
+        .operands_in(vec![N])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let N = &operand("N", boolean);
+    let a = &operand_doc("a", Bool, "A constant boolean scalar or vector value");
+
+    ig.push(
+        Inst::new(
+            "bconst",
+            r#"
+        Boolean constant.
+
+        Create a scalar boolean SSA value with an immediate constant value, or
+        a boolean vector where all the lanes have the same value.
+        "#,
+        )
+        .operands_in(vec![N])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "nop",
+            r#"
+        Just a dummy instruction
+
+        Note: this doesn't compile to a machine code nop
+        "#,
+        )
+        .finish(format_registry),
+    );
+
+    let c = &operand_doc("c", Testable, "Controlling value to test");
+    let x = &operand_doc("x", Any, "Value to use when `c` is true");
+    let y = &operand_doc("y", Any, "Value to use when `c` is false");
+    let a = &operand("a", Any);
+
+    ig.push(
+        Inst::new(
+            "select",
+            r#"
+        Conditional select.
+
+        This instruction selects whole values. Use :inst:`vselect` for
+        lane-wise selection.
+        "#,
+        )
+        .operands_in(vec![c, x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let cc = &operand_doc("cc", intcc, "Controlling condition code");
+    let flags = &operand_doc("flags", iflags, "The machine's flag register");
+
+    ig.push(
+        Inst::new(
+            "selectif",
+            r#"
+        Conditional select, dependent on integer condition codes.
+        "#,
+        )
+        .operands_in(vec![cc, flags, x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", Any);
+
+    ig.push(
+        Inst::new(
+            "copy",
+            r#"
+        Register-register copy.
+
+        This instruction copies its input, preserving the value type.
+
+        A pure SSA-form program does not need to copy values, but this
+        instruction is useful for representing intermediate stages during
+        instruction transformations, and the register allocator needs a way of
+        representing register copies.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "spill",
+            r#"
+        Spill a register value to a stack slot.
+
+        This instruction behaves exactly like :inst:`copy`, but the result
+        value is assigned to a spill slot.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .can_store(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fill",
+            r#"
+        Load a register value from a stack slot.
+
+        This instruction behaves exactly like :inst:`copy`, but creates a new
+        SSA value for the spilled input value.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .can_load(true)
+        .finish(format_registry),
+    );
+
+    let src = &operand("src", regunit);
+    let dst = &operand("dst", regunit);
+
+    ig.push(
+        Inst::new(
+            "regmove",
+            r#"
+        Temporarily divert ``x`` from ``src`` to ``dst``.
+
+        This instruction moves the location of a value from one register to
+        another without creating a new SSA value. It is used by the register
+        allocator to temporarily rearrange register assignments in order to
+        satisfy instruction constraints.
+
+        The register diversions created by this instruction must be undone
+        before the value leaves the EBB. At the entry to a new EBB, all live
+        values must be in their originally assigned registers.
+        "#,
+        )
+        .operands_in(vec![x, src, dst])
+        .other_side_effects(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "copy_special",
+            r#"
+        Copies the contents of ''src'' register to ''dst'' register.
+
+        This instructions copies the contents of one register to another
+        register without involving any SSA values. This is used for copying
+        special registers, e.g. copying the stack register to the frame
+        register in a function prologue.
+        "#,
+        )
+        .operands_in(vec![src, dst])
+        .other_side_effects(true)
+        .finish(format_registry),
+    );
+
+    let delta = &operand("delta", Int);
+
+    ig.push(
+        Inst::new(
+            "adjust_sp_down",
+            r#"
+    Subtracts ``delta`` offset value from the stack pointer register.
+
+    This instruction is used to adjust the stack pointer by a dynamic amount.
+    "#,
+        )
+        .operands_in(vec![delta])
+        .other_side_effects(true)
+        .finish(format_registry),
+    );
+
+    let Offset = &operand_doc("Offset", imm64, "Offset from current stack pointer");
+
+    ig.push(
+        Inst::new(
+            "adjust_sp_up_imm",
+            r#"
+    Adds ``Offset`` immediate offset value to the stack pointer register.
+
+    This instruction is used to adjust the stack pointer, primarily in function
+    prologues and epilogues. ``Offset`` is constrained to the size of a signed
+    32-bit integer.
+    "#,
+        )
+        .operands_in(vec![Offset])
+        .other_side_effects(true)
+        .finish(format_registry),
+    );
+
+    let Offset = &operand_doc("Offset", imm64, "Offset from current stack pointer");
+
+    ig.push(
+        Inst::new(
+            "adjust_sp_down_imm",
+            r#"
+    Subtracts ``Offset`` immediate offset value from the stack pointer
+    register.
+
+    This instruction is used to adjust the stack pointer, primarily in function
+    prologues and epilogues. ``Offset`` is constrained to the size of a signed
+    32-bit integer.
+    "#,
+        )
+        .operands_in(vec![Offset])
+        .other_side_effects(true)
+        .finish(format_registry),
+    );
+
+    let f = &operand("f", iflags);
+
+    ig.push(
+        Inst::new(
+            "ifcmp_sp",
+            r#"
+    Compare ``addr`` with the stack pointer and set the CPU flags.
+
+    This is like :inst:`ifcmp` where ``addr`` is the LHS operand and the stack
+    pointer is the RHS.
+    "#,
+        )
+        .operands_in(vec![addr])
+        .operands_out(vec![f])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "regspill",
+            r#"
+        Temporarily divert ``x`` from ``src`` to ``SS``.
+
+        This instruction moves the location of a value from a register to a
+        stack slot without creating a new SSA value. It is used by the register
+        allocator to temporarily rearrange register assignments in order to
+        satisfy instruction constraints.
+
+        See also :inst:`regmove`.
+        "#,
+        )
+        .operands_in(vec![x, src, SS])
+        .other_side_effects(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "regfill",
+            r#"
+        Temporarily divert ``x`` from ``SS`` to ``dst``.
+
+        This instruction moves the location of a value from a stack slot to a
+        register without creating a new SSA value. It is used by the register
+        allocator to temporarily rearrange register assignments in order to
+        satisfy instruction constraints.
+
+        See also :inst:`regmove`.
+        "#,
+        )
+        .operands_in(vec![x, SS, dst])
+        .other_side_effects(true)
+        .finish(format_registry),
+    );
+
+    let x = &operand_doc("x", TxN, "Vector to split");
+    let lo = &operand_doc("lo", &TxN.half_vector(), "Low-numbered lanes of `x`");
+    let hi = &operand_doc("hi", &TxN.half_vector(), "High-numbered lanes of `x`");
+
+    ig.push(
+        Inst::new(
+            "vsplit",
+            r#"
+        Split a vector into two halves.
+
+        Split the vector `x` into two separate values, each containing half of
+        the lanes from ``x``. The result may be two scalars if ``x`` only had
+        two lanes.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![lo, hi])
+        .is_ghost(true)
+        .finish(format_registry),
+    );
+
+    let Any128 = &TypeVar::new(
+        "Any128",
+        "Any scalar or vector type with as most 128 lanes",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .floats(Interval::All)
+            .bools(Interval::All)
+            .simd_lanes(1..128)
+            .includes_scalars(true)
+            .finish(),
+    );
+
+    let x = &operand_doc("x", Any128, "Low-numbered lanes");
+    let y = &operand_doc("y", Any128, "High-numbered lanes");
+    let a = &operand_doc("a", &Any128.double_vector(), "Concatenation of `x` and `y`");
+
+    ig.push(
+        Inst::new(
+            "vconcat",
+            r#"
+        Vector concatenation.
+
+        Return a vector formed by concatenating ``x`` and ``y``. The resulting
+        vector type has twice as many lanes as each of the inputs. The lanes of
+        ``x`` appear as the low-numbered lanes, and the lanes of ``y`` become
+        the high-numbered lanes of ``a``.
+
+        It is possible to form a vector by concatenating two scalars.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .is_ghost(true)
+        .finish(format_registry),
+    );
+
+    let c = &operand_doc("c", &TxN.as_bool(), "Controlling vector");
+    let x = &operand_doc("x", TxN, "Value to use where `c` is true");
+    let y = &operand_doc("y", TxN, "Value to use where `c` is false");
+    let a = &operand("a", TxN);
+
+    ig.push(
+        Inst::new(
+            "vselect",
+            r#"
+        Vector lane select.
+
+        Select lanes from ``x`` or ``y`` controlled by the lanes of the boolean
+        vector ``c``.
+        "#,
+        )
+        .operands_in(vec![c, x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", &TxN.lane_of());
+
+    ig.push(
+        Inst::new(
+            "splat",
+            r#"
+        Vector splat.
+
+        Return a vector whose lanes are all ``x``.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand_doc("x", TxN, "SIMD vector to modify");
+    let y = &operand_doc("y", &TxN.lane_of(), "New lane value");
+    let Idx = &operand_doc("Idx", uimm8, "Lane index");
+
+    ig.push(
+        Inst::new(
+            "insertlane",
+            r#"
+        Insert ``y`` as lane ``Idx`` in x.
+
+        The lane index, ``Idx``, is an immediate value, not an SSA value. It
+        must indicate a valid lane index for the type of ``x``.
+        "#,
+        )
+        .operands_in(vec![x, Idx, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", TxN);
+    let a = &operand("a", &TxN.lane_of());
+
+    ig.push(
+        Inst::new(
+            "extractlane",
+            r#"
+        Extract lane ``Idx`` from ``x``.
+
+        The lane index, ``Idx``, is an immediate value, not an SSA value. It
+        must indicate a valid lane index for the type of ``x``.
+        "#,
+        )
+        .operands_in(vec![x, Idx])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let a = &operand("a", &Int.as_bool());
+    let Cond = &operand("Cond", intcc);
+    let x = &operand("x", Int);
+    let y = &operand("y", Int);
+
+    ig.push(
+        Inst::new(
+            "icmp",
+            r#"
+        Integer comparison.
+
+        The condition code determines if the operands are interpreted as signed
+        or unsigned integers.
+
+        ====== ======== =========
+        Signed Unsigned Condition
+        ====== ======== =========
+        eq     eq       Equal
+        ne     ne       Not equal
+        slt    ult      Less than
+        sge    uge      Greater than or equal
+        sgt    ugt      Greater than
+        sle    ule      Less than or equal
+        ====== ======== =========
+
+        When this instruction compares integer vectors, it returns a boolean
+        vector of lane-wise comparisons.
+        "#,
+        )
+        .operands_in(vec![Cond, x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let a = &operand("a", b1);
+    let x = &operand("x", iB);
+    let Y = &operand("Y", imm64);
+
+    ig.push(
+        Inst::new(
+            "icmp_imm",
+            r#"
+        Compare scalar integer to a constant.
+
+        This is the same as the :inst:`icmp` instruction, except one operand is
+        an immediate constant.
+
+        This instruction can only compare scalars. Use :inst:`icmp` for
+        lane-wise vector comparisons.
+        "#,
+        )
+        .operands_in(vec![Cond, x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let f = &operand("f", iflags);
+    let x = &operand("x", iB);
+    let y = &operand("y", iB);
+
+    ig.push(
+        Inst::new(
+            "ifcmp",
+            r#"
+        Compare scalar integers and return flags.
+
+        Compare two scalar integer values and return integer CPU flags
+        representing the result.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![f])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "ifcmp_imm",
+            r#"
+        Compare scalar integer to a constant and return flags.
+
+        Like :inst:`icmp_imm`, but returns integer CPU flags instead of testing
+        a specific condition code.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![f])
+        .finish(format_registry),
+    );
+
+    let a = &operand("a", Int);
+    let x = &operand("x", Int);
+    let y = &operand("y", Int);
+
+    ig.push(
+        Inst::new(
+            "iadd",
+            r#"
+        Wrapping integer addition: :math:`a := x + y \pmod{2^B}`.
+
+        This instruction does not depend on the signed/unsigned interpretation
+        of the operands.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "isub",
+            r#"
+        Wrapping integer subtraction: :math:`a := x - y \pmod{2^B}`.
+
+        This instruction does not depend on the signed/unsigned interpretation
+        of the operands.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "imul",
+            r#"
+        Wrapping integer multiplication: :math:`a := x y \pmod{2^B}`.
+
+        This instruction does not depend on the signed/unsigned interpretation
+        of the
+        operands.
+
+        Polymorphic over all integer types (vector and scalar).
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "umulhi",
+            r#"
+        Unsigned integer multiplication, producing the high half of a
+        double-length result.
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "smulhi",
+            r#"
+        Signed integer multiplication, producing the high half of a
+        double-length result.
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "udiv",
+            r#"
+        Unsigned integer division: :math:`a := \lfloor {x \over y} \rfloor`.
+
+        This operation traps if the divisor is zero.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sdiv",
+            r#"
+        Signed integer division rounded toward zero: :math:`a := sign(xy)
+        \lfloor {|x| \over |y|}\rfloor`.
+
+        This operation traps if the divisor is zero, or if the result is not
+        representable in :math:`B` bits two's complement. This only happens
+        when :math:`x = -2^{B-1}, y = -1`.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "urem",
+            r#"
+        Unsigned integer remainder.
+
+        This operation traps if the divisor is zero.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "srem",
+            r#"
+        Signed integer remainder. The result has the sign of the dividend.
+
+        This operation traps if the divisor is zero.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    let a = &operand("a", iB);
+    let x = &operand("x", iB);
+    let Y = &operand("Y", imm64);
+
+    ig.push(
+        Inst::new(
+            "iadd_imm",
+            r#"
+        Add immediate integer.
+
+        Same as :inst:`iadd`, but one operand is an immediate constant.
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "imul_imm",
+            r#"
+        Integer multiplication by immediate constant.
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "udiv_imm",
+            r#"
+        Unsigned integer division by an immediate constant.
+
+        This operation traps if the divisor is zero.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sdiv_imm",
+            r#"
+        Signed integer division by an immediate constant.
+
+        This operation traps if the divisor is zero, or if the result is not
+        representable in :math:`B` bits two's complement. This only happens
+        when :math:`x = -2^{B-1}, Y = -1`.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "urem_imm",
+            r#"
+        Unsigned integer remainder with immediate divisor.
+
+        This operation traps if the divisor is zero.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "srem_imm",
+            r#"
+        Signed integer remainder with immediate divisor.
+
+        This operation traps if the divisor is zero.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "irsub_imm",
+            r#"
+        Immediate reverse wrapping subtraction: :math:`a := Y - x \pmod{2^B}`.
+
+        Also works as integer negation when :math:`Y = 0`. Use :inst:`iadd_imm`
+        with a negative immediate operand for the reverse immediate
+        subtraction.
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let a = &operand("a", iB);
+    let x = &operand("x", iB);
+    let y = &operand("y", iB);
+    let c_in = &operand_doc("c_in", b1, "Input carry flag");
+    let c_out = &operand_doc("c_out", b1, "Output carry flag");
+    let b_in = &operand_doc("b_in", b1, "Input borrow flag");
+    let b_out = &operand_doc("b_out", b1, "Output borrow flag");
+
+    ig.push(
+        Inst::new(
+            "iadd_cin",
+            r#"
+        Add integers with carry in.
+
+        Same as :inst:`iadd` with an additional carry input. Computes:
+
+        .. math::
+
+            a = x + y + c_{in} \pmod 2^B
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, y, c_in])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "iadd_cout",
+            r#"
+        Add integers with carry out.
+
+        Same as :inst:`iadd` with an additional carry output.
+
+        .. math::
+
+            a &= x + y \pmod 2^B \\
+            c_{out} &= x+y >= 2^B
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a, c_out])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "iadd_carry",
+            r#"
+        Add integers with carry in and out.
+
+        Same as :inst:`iadd` with an additional carry input and output.
+
+        .. math::
+
+            a &= x + y + c_{in} \pmod 2^B \\
+            c_{out} &= x + y + c_{in} >= 2^B
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, y, c_in])
+        .operands_out(vec![a, c_out])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "isub_bin",
+            r#"
+        Subtract integers with borrow in.
+
+        Same as :inst:`isub` with an additional borrow flag input. Computes:
+
+        .. math::
+
+            a = x - (y + b_{in}) \pmod 2^B
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, y, b_in])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "isub_bout",
+            r#"
+        Subtract integers with borrow out.
+
+        Same as :inst:`isub` with an additional borrow flag output.
+
+        .. math::
+
+            a &= x - y \pmod 2^B \\
+            b_{out} &= x < y
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a, b_out])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "isub_borrow",
+            r#"
+        Subtract integers with borrow in and out.
+
+        Same as :inst:`isub` with an additional borrow flag input and output.
+
+        .. math::
+
+            a &= x - (y + b_{in}) \pmod 2^B \\
+            b_{out} &= x < y + b_{in}
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, y, b_in])
+        .operands_out(vec![a, b_out])
+        .finish(format_registry),
+    );
+
+    let bits = &TypeVar::new(
+        "bits",
+        "Any integer, float, or boolean scalar or vector type",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .floats(Interval::All)
+            .bools(Interval::All)
+            .simd_lanes(Interval::All)
+            .includes_scalars(true)
+            .finish(),
+    );
+    let x = &operand("x", bits);
+    let y = &operand("y", bits);
+    let a = &operand("a", bits);
+
+    ig.push(
+        Inst::new(
+            "band",
+            r#"
+        Bitwise and.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "bor",
+            r#"
+        Bitwise or.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "bxor",
+            r#"
+        Bitwise xor.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "bnot",
+            r#"
+        Bitwise not.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "band_not",
+            r#"
+        Bitwise and not.
+
+        Computes `x & ~y`.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "bor_not",
+            r#"
+        Bitwise or not.
+
+        Computes `x | ~y`.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "bxor_not",
+            r#"
+        Bitwise xor not.
+
+        Computes `x ^ ~y`.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", iB);
+    let Y = &operand("Y", imm64);
+    let a = &operand("a", iB);
+
+    ig.push(
+        Inst::new(
+            "band_imm",
+            r#"
+        Bitwise and with immediate.
+
+        Same as :inst:`band`, but one operand is an immediate constant.
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "bor_imm",
+            r#"
+        Bitwise or with immediate.
+
+        Same as :inst:`bor`, but one operand is an immediate constant.
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "bxor_imm",
+            r#"
+        Bitwise xor with immediate.
+
+        Same as :inst:`bxor`, but one operand is an immediate constant.
+
+        Polymorphic over all scalar integer types, but does not support vector
+        types.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand_doc("x", Int, "Scalar or vector value to shift");
+    let y = &operand_doc("y", iB, "Number of bits to shift");
+    let Y = &operand("Y", imm64);
+    let a = &operand("a", Int);
+
+    ig.push(
+        Inst::new(
+            "rotl",
+            r#"
+        Rotate left.
+
+        Rotate the bits in ``x`` by ``y`` places.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "rotr",
+            r#"
+        Rotate right.
+
+        Rotate the bits in ``x`` by ``y`` places.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "rotl_imm",
+            r#"
+        Rotate left by immediate.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "rotr_imm",
+            r#"
+        Rotate right by immediate.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "ishl",
+            r#"
+        Integer shift left. Shift the bits in ``x`` towards the MSB by ``y``
+        places. Shift in zero bits to the LSB.
+
+        The shift amount is masked to the size of ``x``.
+
+        When shifting a B-bits integer type, this instruction computes:
+
+        .. math::
+            s &:= y \pmod B,                \\
+            a &:= x \cdot 2^s \pmod{2^B}.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "ushr",
+            r#"
+        Unsigned shift right. Shift bits in ``x`` towards the LSB by ``y``
+        places, shifting in zero bits to the MSB. Also called a *logical
+        shift*.
+
+        The shift amount is masked to the size of the register.
+
+        When shifting a B-bits integer type, this instruction computes:
+
+        .. math::
+            s &:= y \pmod B,                \\
+            a &:= \lfloor x \cdot 2^{-s} \rfloor.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sshr",
+            r#"
+        Signed shift right. Shift bits in ``x`` towards the LSB by ``y``
+        places, shifting in sign bits to the MSB. Also called an *arithmetic
+        shift*.
+
+        The shift amount is masked to the size of the register.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "ishl_imm",
+            r#"
+        Integer shift left by immediate.
+
+        The shift amount is masked to the size of ``x``.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "ushr_imm",
+            r#"
+        Unsigned shift right by immediate.
+
+        The shift amount is masked to the size of the register.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sshr_imm",
+            r#"
+        Signed shift right by immediate.
+
+        The shift amount is masked to the size of the register.
+        "#,
+        )
+        .operands_in(vec![x, Y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", iB);
+    let a = &operand("a", iB);
+
+    ig.push(
+        Inst::new(
+            "bitrev",
+            r#"
+        Reverse the bits of a integer.
+
+        Reverses the bits in ``x``.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "clz",
+            r#"
+        Count leading zero bits.
+
+        Starting from the MSB in ``x``, count the number of zero bits before
+        reaching the first one bit. When ``x`` is zero, returns the size of x
+        in bits.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "cls",
+            r#"
+        Count leading sign bits.
+
+        Starting from the MSB after the sign bit in ``x``, count the number of
+        consecutive bits identical to the sign bit. When ``x`` is 0 or -1,
+        returns one less than the size of x in bits.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "ctz",
+            r#"
+        Count trailing zeros.
+
+        Starting from the LSB in ``x``, count the number of zero bits before
+        reaching the first one bit. When ``x`` is zero, returns the size of x
+        in bits.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "popcnt",
+            r#"
+        Population count
+
+        Count the number of one bits in ``x``.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let Float = &TypeVar::new(
+        "Float",
+        "A scalar or vector floating point number",
+        TypeSetBuilder::new()
+            .floats(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+    let Cond = &operand("Cond", floatcc);
+    let x = &operand("x", Float);
+    let y = &operand("y", Float);
+    let a = &operand("a", &Float.as_bool());
+
+    ig.push(
+        Inst::new(
+            "fcmp",
+            r#"
+        Floating point comparison.
+
+        Two IEEE 754-2008 floating point numbers, `x` and `y`, relate to each
+        other in exactly one of four ways:
+
+        == ==========================================
+        UN Unordered when one or both numbers is NaN.
+        EQ When :math:`x = y`. (And :math:`0.0 = -0.0`).
+        LT When :math:`x < y`.
+        GT When :math:`x > y`.
+        == ==========================================
+
+        The 14 :type:`floatcc` condition codes each correspond to a subset of
+        the four relations, except for the empty set which would always be
+        false, and the full set which would always be true.
+
+        The condition codes are divided into 7 'ordered' conditions which don't
+        include UN, and 7 unordered conditions which all include UN.
+
+        +-------+------------+---------+------------+-------------------------+
+        |Ordered             |Unordered             |Condition                |
+        +=======+============+=========+============+=========================+
+        |ord    |EQ | LT | GT|uno      |UN          |NaNs absent / present.   |
+        +-------+------------+---------+------------+-------------------------+
+        |eq     |EQ          |ueq      |UN | EQ     |Equal                    |
+        +-------+------------+---------+------------+-------------------------+
+        |one    |LT | GT     |ne       |UN | LT | GT|Not equal                |
+        +-------+------------+---------+------------+-------------------------+
+        |lt     |LT          |ult      |UN | LT     |Less than                |
+        +-------+------------+---------+------------+-------------------------+
+        |le     |LT | EQ     |ule      |UN | LT | EQ|Less than or equal       |
+        +-------+------------+---------+------------+-------------------------+
+        |gt     |GT          |ugt      |UN | GT     |Greater than             |
+        +-------+------------+---------+------------+-------------------------+
+        |ge     |GT | EQ     |uge      |UN | GT | EQ|Greater than or equal    |
+        +-------+------------+---------+------------+-------------------------+
+
+        The standard C comparison operators, `<, <=, >, >=`, are all ordered,
+        so they are false if either operand is NaN. The C equality operator,
+        `==`, is ordered, and since inequality is defined as the logical
+        inverse it is *unordered*. They map to the :type:`floatcc` condition
+        codes as follows:
+
+        ==== ====== ============
+        C    `Cond` Subset
+        ==== ====== ============
+        `==` eq     EQ
+        `!=` ne     UN | LT | GT
+        `<`  lt     LT
+        `<=` le     LT | EQ
+        `>`  gt     GT
+        `>=` ge     GT | EQ
+        ==== ====== ============
+
+        This subset of condition codes also corresponds to the WebAssembly
+        floating point comparisons of the same name.
+
+        When this instruction compares floating point vectors, it returns a
+        boolean vector with the results of lane-wise comparisons.
+        "#,
+        )
+        .operands_in(vec![Cond, x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let f = &operand("f", fflags);
+
+    ig.push(
+        Inst::new(
+            "ffcmp",
+            r#"
+        Floating point comparison returning flags.
+
+        Compares two numbers like :inst:`fcmp`, but returns floating point CPU
+        flags instead of testing a specific condition.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![f])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", Float);
+    let y = &operand("y", Float);
+    let z = &operand("z", Float);
+    let a = &operand_doc("a", Float, "Result of applying operator to each lane");
+
+    ig.push(
+        Inst::new(
+            "fadd",
+            r#"
+        Floating point addition.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fsub",
+            r#"
+        Floating point subtraction.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fmul",
+            r#"
+        Floating point multiplication.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fdiv",
+            r#"
+        Floating point division.
+
+        Unlike the integer division instructions :clif:inst:`sdiv` and
+        :clif:inst:`udiv`, this can't trap. Division by zero is infinity or
+        NaN, depending on the dividend.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sqrt",
+            r#"
+        Floating point square root.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fma",
+            r#"
+        Floating point fused multiply-and-add.
+
+        Computes :math:`a := xy+z` without any intermediate rounding of the
+        product.
+        "#,
+        )
+        .operands_in(vec![x, y, z])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let a = &operand_doc("a", Float, "``x`` with its sign bit inverted");
+
+    ig.push(
+        Inst::new(
+            "fneg",
+            r#"
+        Floating point negation.
+
+        Note that this is a pure bitwise operation.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let a = &operand_doc("a", Float, "``x`` with its sign bit cleared");
+
+    ig.push(
+        Inst::new(
+            "fabs",
+            r#"
+        Floating point absolute value.
+
+        Note that this is a pure bitwise operation.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let a = &operand_doc(
+        "a",
+        Float,
+        "``x`` with its sign bit changed to that of ``y``",
+    );
+
+    ig.push(
+        Inst::new(
+            "fcopysign",
+            r#"
+        Floating point copy sign.
+
+        Note that this is a pure bitwise operation. The sign bit from ``y`` is
+        copied to the sign bit of ``x``.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let a = &operand_doc("a", Float, "The smaller of ``x`` and ``y``");
+
+    ig.push(
+        Inst::new(
+            "fmin",
+            r#"
+        Floating point minimum, propagating NaNs.
+
+        If either operand is NaN, this returns a NaN.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let a = &operand_doc("a", Float, "The larger of ``x`` and ``y``");
+
+    ig.push(
+        Inst::new(
+            "fmax",
+            r#"
+        Floating point maximum, propagating NaNs.
+
+        If either operand is NaN, this returns a NaN.
+        "#,
+        )
+        .operands_in(vec![x, y])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let a = &operand_doc("a", Float, "``x`` rounded to integral value");
+
+    ig.push(
+        Inst::new(
+            "ceil",
+            r#"
+        Round floating point round to integral, towards positive infinity.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "floor",
+            r#"
+        Round floating point round to integral, towards negative infinity.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "trunc",
+            r#"
+        Round floating point round to integral, towards zero.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "nearest",
+            r#"
+        Round floating point round to integral, towards nearest with ties to
+        even.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let Cond = &operand("Cond", intcc);
+    let f = &operand("f", iflags);
+    let a = &operand("a", b1);
+
+    ig.push(
+        Inst::new(
+            "trueif",
+            r#"
+        Test integer CPU flags for a specific condition.
+
+        Check the CPU flags in ``f`` against the ``Cond`` condition code and
+        return true when the condition code is satisfied.
+        "#,
+        )
+        .operands_in(vec![Cond, f])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let Cond = &operand("Cond", floatcc);
+    let f = &operand("f", fflags);
+
+    ig.push(
+        Inst::new(
+            "trueff",
+            r#"
+        Test floating point CPU flags for a specific condition.
+
+        Check the CPU flags in ``f`` against the ``Cond`` condition code and
+        return true when the condition code is satisfied.
+        "#,
+        )
+        .operands_in(vec![Cond, f])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", Mem);
+    let a = &operand_doc("a", MemTo, "Bits of `x` reinterpreted");
+
+    ig.push(
+        Inst::new(
+            "bitcast",
+            r#"
+        Reinterpret the bits in `x` as a different type.
+
+        The input and output types must be storable to memory and of the same
+        size. A bitcast is equivalent to storing one type and loading the other
+        type from the same address.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let Bool = &TypeVar::new(
+        "Bool",
+        "A scalar or vector boolean type",
+        TypeSetBuilder::new()
+            .bools(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+
+    let BoolTo = &TypeVar::new(
+        "BoolTo",
+        "A smaller boolean type with the same number of lanes",
+        TypeSetBuilder::new()
+            .bools(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+
+    let x = &operand("x", Bool);
+    let a = &operand("a", BoolTo);
+
+    ig.push(
+        Inst::new(
+            "breduce",
+            r#"
+        Convert `x` to a smaller boolean type in the platform-defined way.
+
+        The result type must have the same number of vector lanes as the input,
+        and each lane must not have more bits that the input lanes. If the
+        input and output types are the same, this is a no-op.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .constraints(vec![WiderOrEq(Bool.clone(), BoolTo.clone())])
+        .finish(format_registry),
+    );
+
+    let BoolTo = &TypeVar::new(
+        "BoolTo",
+        "A larger boolean type with the same number of lanes",
+        TypeSetBuilder::new()
+            .bools(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+    let x = &operand("x", Bool);
+    let a = &operand("a", BoolTo);
+
+    ig.push(
+        Inst::new(
+            "bextend",
+            r#"
+        Convert `x` to a larger boolean type in the platform-defined way.
+
+        The result type must have the same number of vector lanes as the input,
+        and each lane must not have fewer bits that the input lanes. If the
+        input and output types are the same, this is a no-op.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .constraints(vec![WiderOrEq(BoolTo.clone(), Bool.clone())])
+        .finish(format_registry),
+    );
+
+    let IntTo = &TypeVar::new(
+        "IntTo",
+        "An integer type with the same number of lanes",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+    let x = &operand("x", Bool);
+    let a = &operand("a", IntTo);
+
+    ig.push(
+        Inst::new(
+            "bint",
+            r#"
+        Convert `x` to an integer.
+
+        True maps to 1 and false maps to 0. The result type must have the same
+        number of vector lanes as the input.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "bmask",
+            r#"
+        Convert `x` to an integer mask.
+
+        True maps to all 1s and false maps to all 0s. The result type must have
+        the same number of vector lanes as the input.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let Int = &TypeVar::new(
+        "Int",
+        "A scalar or vector integer type",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+
+    let IntTo = &TypeVar::new(
+        "IntTo",
+        "A smaller integer type with the same number of lanes",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+    let x = &operand("x", Int);
+    let a = &operand("a", IntTo);
+
+    ig.push(
+        Inst::new(
+            "ireduce",
+            r#"
+        Convert `x` to a smaller integer type by dropping high bits.
+
+        Each lane in `x` is converted to a smaller integer type by discarding
+        the most significant bits. This is the same as reducing modulo
+        :math:`2^n`.
+
+        The result type must have the same number of vector lanes as the input,
+        and each lane must not have more bits that the input lanes. If the
+        input and output types are the same, this is a no-op.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .constraints(vec![WiderOrEq(Int.clone(), IntTo.clone())])
+        .finish(format_registry),
+    );
+
+    let IntTo = &TypeVar::new(
+        "IntTo",
+        "A larger integer type with the same number of lanes",
+        TypeSetBuilder::new()
+            .ints(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+    let x = &operand("x", Int);
+    let a = &operand("a", IntTo);
+
+    ig.push(
+        Inst::new(
+            "uextend",
+            r#"
+        Convert `x` to a larger integer type by zero-extending.
+
+        Each lane in `x` is converted to a larger integer type by adding
+        zeroes. The result has the same numerical value as `x` when both are
+        interpreted as unsigned integers.
+
+        The result type must have the same number of vector lanes as the input,
+        and each lane must not have fewer bits that the input lanes. If the
+        input and output types are the same, this is a no-op.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .constraints(vec![WiderOrEq(IntTo.clone(), Int.clone())])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "sextend",
+            r#"
+        Convert `x` to a larger integer type by sign-extending.
+
+        Each lane in `x` is converted to a larger integer type by replicating
+        the sign bit. The result has the same numerical value as `x` when both
+        are interpreted as signed integers.
+
+        The result type must have the same number of vector lanes as the input,
+        and each lane must not have fewer bits that the input lanes. If the
+        input and output types are the same, this is a no-op.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .constraints(vec![WiderOrEq(IntTo.clone(), Int.clone())])
+        .finish(format_registry),
+    );
+
+    let FloatTo = &TypeVar::new(
+        "FloatTo",
+        "A scalar or vector floating point number",
+        TypeSetBuilder::new()
+            .floats(Interval::All)
+            .simd_lanes(Interval::All)
+            .finish(),
+    );
+    let x = &operand("x", Float);
+    let a = &operand("a", FloatTo);
+
+    ig.push(
+        Inst::new(
+            "fpromote",
+            r#"
+        Convert `x` to a larger floating point format.
+
+        Each lane in `x` is converted to the destination floating point format.
+        This is an exact operation.
+
+        Cranelift currently only supports two floating point formats
+        - :type:`f32` and :type:`f64`. This may change in the future.
+
+        The result type must have the same number of vector lanes as the input,
+        and the result lanes must not have fewer bits than the input lanes. If
+        the input and output types are the same, this is a no-op.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .constraints(vec![WiderOrEq(FloatTo.clone(), Float.clone())])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fdemote",
+            r#"
+        Convert `x` to a smaller floating point format.
+
+        Each lane in `x` is converted to the destination floating point format
+        by rounding to nearest, ties to even.
+
+        Cranelift currently only supports two floating point formats
+        - :type:`f32` and :type:`f64`. This may change in the future.
+
+        The result type must have the same number of vector lanes as the input,
+        and the result lanes must not have more bits than the input lanes. If
+        the input and output types are the same, this is a no-op.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .constraints(vec![WiderOrEq(Float.clone(), FloatTo.clone())])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", Float);
+    let a = &operand("a", IntTo);
+
+    ig.push(
+        Inst::new(
+            "fcvt_to_uint",
+            r#"
+        Convert floating point to unsigned integer.
+
+        Each lane in `x` is converted to an unsigned integer by rounding
+        towards zero. If `x` is NaN or if the unsigned integral value cannot be
+        represented in the result type, this instruction traps.
+
+        The result type must have the same number of vector lanes as the input.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fcvt_to_uint_sat",
+            r#"
+        Convert floating point to unsigned integer as fcvt_to_uint does, but
+        saturates the input instead of trapping. NaN and negative values are
+        converted to 0.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fcvt_to_sint",
+            r#"
+        Convert floating point to signed integer.
+
+        Each lane in `x` is converted to a signed integer by rounding towards
+        zero. If `x` is NaN or if the signed integral value cannot be
+        represented in the result type, this instruction traps.
+
+        The result type must have the same number of vector lanes as the input.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .can_trap(true)
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fcvt_to_sint_sat",
+            r#"
+        Convert floating point to signed integer as fcvt_to_sint does, but
+        saturates the input instead of trapping. NaN values are converted to 0.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    let x = &operand("x", Int);
+    let a = &operand("a", FloatTo);
+
+    ig.push(
+        Inst::new(
+            "fcvt_from_uint",
+            r#"
+        Convert unsigned integer to floating point.
+
+        Each lane in `x` is interpreted as an unsigned integer and converted to
+        floating point using round to nearest, ties to even.
+
+        The result type must have the same number of vector lanes as the input.
+        "#,
+        )
+        .operands_in(vec![x])
+        .operands_out(vec![a])
+        .finish(format_registry),
+    );
+
+    ig.push(
+        Inst::new(
+            "fcvt_from_sint",
+            r#"
+        Convert signed integer to floating point.
+
+        Each lane in `x` is interpreted as a signed integer and converted to
+        floating point using round to nearest, ties to even.
+