Merge mozilla-central to mozilla-inbound. CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Mon, 15 Apr 2019 18:28:30 +0300
changeset 469600 a2b89a27e8eb
parent 469599 65e46003724d (current diff)
parent 469488 16d953cca414 (diff)
child 469601 0fd463c60290
push id35875
push userccoroiu@mozilla.com
push dateTue, 16 Apr 2019 04:06:16 +0000
treeherdermozilla-central@a83cab75b00d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. CLOSED TREE
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/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -213,17 +213,17 @@ static void PaintTextShadowCallback(gfxC
                                     const nscolor& aShadowColor, void* aData) {
   reinterpret_cast<nsDisplayTextOverflowMarker*>(aData)->PaintTextToContext(
       aCtx, aShadowOffset);
 }
 
 void nsDisplayTextOverflowMarker::Paint(nsDisplayListBuilder* aBuilder,
                                         gfxContext* aCtx) {
   DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
-                                                    IsSubpixelAADisabled());
+                                                    mDisableSubpixelAA);
 
   nscolor foregroundColor =
       nsLayoutUtils::GetColor(mFrame, &nsStyleText::mWebkitTextFillColor);
 
   // Paint the text-shadows for the overflow marker
   nsLayoutUtils::PaintTextShadow(mFrame, aCtx, mRect, GetPaintRect(),
                                  foregroundColor, PaintTextShadowCallback,
                                  (void*)this);
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -101,21 +101,18 @@ static void BuildDisplayListForTopLayerF
     // root scroll frame.
     clipState.SetClipChainForContainingBlockDescendants(
         savedOutOfFlowData->mCombinedClipChain);
     clipState.ClipContainingBlockDescendantsExtra(
         visible + aBuilder->ToReferenceFrame(aFrame), nullptr);
     asrSetter.SetCurrentActiveScrolledRoot(
         savedOutOfFlowData->mContainingBlockActiveScrolledRoot);
   }
-  // This function jumps into random frames that may not be descendants of
-  // aBuilder->mCurrentFrame, so aBuilder->mInInvalidSubtree is unrelated.
-  // Request recalculation of mInInvalidSubtree.
   nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
-      aBuilder, aFrame, visible, dirty, nsDisplayListBuilder::RIIS_YES);
+      aBuilder, aFrame, visible, dirty);
 
   nsDisplayList list;
   aFrame->BuildDisplayListForStackingContext(aBuilder, &list);
   aList->AppendToTop(&list);
 }
 
 void ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
                                                 nsDisplayList* aList) {
@@ -151,20 +148,18 @@ void ViewportFrame::BuildDisplayListForT
               frame->GetChildList(kBackdropList).FirstChild()) {
         MOZ_ASSERT(backdropPh->IsPlaceholderFrame());
         MOZ_ASSERT(!backdropPh->GetNextSibling(), "more than one ::backdrop?");
         MOZ_ASSERT(backdropPh->HasAnyStateBits(NS_FRAME_FIRST_REFLOW),
                    "did you intend to reflow ::backdrop placeholders?");
         nsIFrame* backdropFrame =
             static_cast<nsPlaceholderFrame*>(backdropPh)->GetOutOfFlowFrame();
         MOZ_ASSERT(backdropFrame);
-
         BuildDisplayListForTopLayerFrame(aBuilder, backdropFrame, aList);
       }
-
       BuildDisplayListForTopLayerFrame(aBuilder, frame, aList);
     }
   }
 
   if (nsCanvasFrame* canvasFrame = PresShell()->GetCanvasFrame()) {
     if (Element* container = canvasFrame->GetCustomContentContainer()) {
       if (nsIFrame* frame = container->GetPrimaryFrame()) {
         MOZ_ASSERT(frame->StyleDisplay()->mTopLayer != NS_STYLE_TOP_LAYER_NONE,
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -641,17 +641,17 @@ bool nsDisplayBullet::CreateWebRenderCom
 
 void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
   uint32_t flags = imgIContainer::FLAG_NONE;
   if (aBuilder->ShouldSyncDecodeImages()) {
     flags |= imgIContainer::FLAG_SYNC_DECODE;
   }
 
   ImgDrawResult result = static_cast<nsBulletFrame*>(mFrame)->PaintBullet(
-      *aCtx, ToReferenceFrame(), GetPaintRect(), flags, IsSubpixelAADisabled());
+      *aCtx, ToReferenceFrame(), GetPaintRect(), flags, mDisableSubpixelAA);
 
   nsDisplayBulletGeometry::UpdateDrawResult(this, result);
 }
 
 void nsBulletFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                      const nsDisplayListSet& aLists) {
   if (!IsVisibleForPainting()) return;
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3802,25 +3802,18 @@ void nsIFrame::BuildDisplayListForChild(
        IsSVGContentWithCSSClip(child))) {
     pseudoStackingContext = true;
     awayFromCommonPath = true;
   }
 
   NS_ASSERTION(!isStackingContext || pseudoStackingContext,
                "Stacking contexts must also be pseudo-stacking-contexts");
 
-  // nsBlockFrame paints pushed floats directly, rather than through their
-  // placeholder, which is why we force a recallculation of InInvalidSubtree
-  // state.
-  auto recalcInInvalidSubtree =
-      (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
-          ? nsDisplayListBuilder::RIIS_YES
-          : nsDisplayListBuilder::RIIS_NO;
   nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
-      aBuilder, child, visible, dirty, recalcInInvalidSubtree);
+      aBuilder, child, visible, dirty);
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
   nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
   CheckForApzAwareEventHandlers(aBuilder, child);
 
   if (savedOutOfFlowData) {
     aBuilder->SetBuildingInvisibleItems(false);
 
     clipState.SetClipChainForContainingBlockDescendants(
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3759,22 +3759,16 @@ void ScrollFrameHelper::BuildDisplayList
 
 void ScrollFrameHelper::MaybeAddTopLayerItems(nsDisplayListBuilder* aBuilder,
                                               const nsDisplayListSet& aLists) {
   if (mIsRoot) {
     if (ViewportFrame* viewportFrame = do_QueryFrame(mOuter->GetParent())) {
       nsDisplayList topLayerList;
       viewportFrame->BuildDisplayListForTopLayer(aBuilder, &topLayerList);
       if (!topLayerList.IsEmpty()) {
-        // This function jumps into random frames that may not be descendants of
-        // aBuilder->mCurrentFrame, so aBuilder->mInInvalidSubtree is unrelated.
-        // Request recalculation of mInInvalidSubtree.
-        nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
-            aBuilder, viewportFrame, nsDisplayListBuilder::RIIS_YES);
-
         // Wrap the whole top layer in a single item with maximum z-index,
         // and append it at the very end, so that it stays at the topmost.
         nsDisplayWrapList* wrapList = MakeDisplayItem<nsDisplayWrapList>(
             aBuilder, viewportFrame, &topLayerList,
             aBuilder->CurrentActiveScrolledRoot(), false, 2);
         if (wrapList) {
           wrapList->SetOverrideZIndex(
               std::numeric_limits<decltype(wrapList->ZIndex())>::max());
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -452,17 +452,17 @@ class nsDisplayHeaderFooter final : publ
 
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      gfxContext* aCtx) override {
 #ifdef DEBUG
     nsPageFrame* pageFrame = do_QueryFrame(mFrame);
     MOZ_ASSERT(pageFrame, "We should have an nsPageFrame");
 #endif
     static_cast<nsPageFrame*>(mFrame)->PaintHeaderFooter(
-        *aCtx, ToReferenceFrame(), IsSubpixelAADisabled());
+        *aCtx, ToReferenceFrame(), mDisableSubpixelAA);
   }
   NS_DISPLAY_DECL_NAME("HeaderFooter", TYPE_HEADER_FOOTER)
 
   virtual nsRect GetComponentAlphaBounds(
       nsDisplayListBuilder* aBuilder) const override {
     bool snap;
     return GetBounds(aBuilder, &snap);
   }
@@ -519,22 +519,18 @@ void nsPageFrame::BuildDisplayList(nsDis
     // we don't have to process earlier pages. The display lists for
     // these extra pages are pruned so that only display items for the
     // page we currently care about (which we would have reached by
     // following placeholders to their out-of-flows) end up on the list.
     nsIFrame* page = child;
     while ((page = GetNextPage(page)) != nullptr) {
       nsRect childVisible = visibleRect + child->GetOffsetTo(page);
 
-      // This function jumps into random frames that may not be descendants of
-      // aBuilder->mCurrentFrame, so aBuilder->mInInvalidSubtree is unrelated.
-      // Request recalculation of mInInvalidSubtree.
       nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
-          aBuilder, page, childVisible, childVisible,
-          nsDisplayListBuilder::RIIS_YES);
+          aBuilder, page, childVisible, childVisible);
       BuildDisplayListForExtraPage(aBuilder, this, page, &content);
     }
 
     // Invoke AutoBuildingDisplayList to ensure that the correct visibleRect
     // is used to compute the visible rect if AddCanvasBackgroundColorItem
     // creates a display item.
     nsDisplayListBuilder::AutoBuildingDisplayList building(
         aBuilder, child, visibleRect, visibleRect);
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -430,25 +430,24 @@ void nsSubDocumentFrame::BuildDisplayLis
   bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
   bool needsOwnLayer = false;
   if (constructResolutionItem || constructZoomItem || haveDisplayPort ||
       presContext->IsRootContentDocument() ||
       (sf && sf->IsScrollingActive(aBuilder))) {
     needsOwnLayer = true;
   }
 
-  if (subdocRootFrame && aBuilder->IsRetainingDisplayList()) {
+  if (aBuilder->IsRetainingDisplayList()) {
     // Caret frame changed, rebuild the entire subdoc.
     // We could just invalidate the old and new frame
     // areas and save some work here. RetainedDisplayListBuilder
     // does this, so we could teach it to find and check all
     // subdocs in advance.
     if (mPreviousCaret != aBuilder->GetCaretFrame()) {
       dirty = visible;
-      aBuilder->MarkFrameModifiedDuringBuilding(subdocRootFrame);
       aBuilder->RebuildAllItemsInCurrentSubtree();
       // Mark the old caret frame as invalid so that we remove the
       // old nsDisplayCaret. We don't mark the current frame as invalid
       // since we want the nsDisplaySubdocument to retain it's place
       // in the retained display list.
       if (mPreviousCaret) {
         aBuilder->MarkFrameModifiedDuringBuilding(mPreviousCaret);
       }
--- a/layout/ipc/RenderFrame.cpp
+++ b/layout/ipc/RenderFrame.cpp
@@ -190,16 +190,21 @@ mozilla::LayerState nsDisplayRemote::Get
     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
     const ContainerLayerParameters& aParameters) {
   if (IsTempLayerManager(aManager)) {
     return mozilla::LAYER_NONE;
   }
   return mozilla::LAYER_ACTIVE_FORCE;
 }
 
+bool nsDisplayRemote::HasDeletedFrame() const {
+  // RenderFrame might change without invalidating nsSubDocumentFrame.
+  return !GetFrameLoader() || nsDisplayItem::HasDeletedFrame();
+}
+
 already_AddRefed<Layer> nsDisplayRemote::BuildLayer(
     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
     const ContainerLayerParameters& aContainerParameters) {
   MOZ_ASSERT(mFrame, "Makes no sense to have a shadow tree without a frame");
 
   if (IsTempLayerManager(aManager)) {
     // This can happen if aManager is a "temporary" manager, or if the
     // widget's layer manager changed out from under us.  We need to
--- a/layout/ipc/RenderFrame.h
+++ b/layout/ipc/RenderFrame.h
@@ -85,31 +85,31 @@ class RenderFrame final {
 }  // namespace layout
 }  // namespace mozilla
 
 /**
  * A nsDisplayRemote will graft a remote frame's shadow layer tree (for a given
  * nsFrameLoader) into its parent frame's layer tree.
  */
 class nsDisplayRemote final : public nsDisplayItem {
-  friend class nsDisplayItem;
-
   typedef mozilla::dom::TabId TabId;
   typedef mozilla::gfx::Matrix4x4 Matrix4x4;
   typedef mozilla::layers::EventRegionsOverride EventRegionsOverride;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayersId LayersId;
   typedef mozilla::layers::RefLayer RefLayer;
   typedef mozilla::layout::RenderFrame RenderFrame;
   typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
   typedef mozilla::LayoutDeviceIntPoint LayoutDeviceIntPoint;
 
  public:
   nsDisplayRemote(nsDisplayListBuilder* aBuilder, nsSubDocumentFrame* aFrame);
 
+  bool HasDeletedFrame() const override;
+
   LayerState GetLayerState(
       nsDisplayListBuilder* aBuilder, LayerManager* aManager,
       const ContainerLayerParameters& aParameters) override;
 
   already_AddRefed<Layer> BuildLayer(
       nsDisplayListBuilder* aBuilder, LayerManager* aManager,
       const ContainerLayerParameters& aContainerParameters) override;
 
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -136,20 +136,21 @@ bool RetainedDisplayListBuilder::PreProc
 
   MOZ_RELEASE_ASSERT(initializeDAG || aList->mDAG.Length() == aList->Count());
 
   nsDisplayList saved;
   aList->mOldItems.SetCapacity(aList->Count());
   MOZ_RELEASE_ASSERT(aList->mOldItems.IsEmpty());
   while (nsDisplayItem* item = aList->RemoveBottom()) {
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-    item->SetMergedPreProcessed(false, true);
+    item->mMergedItem = false;
+    item->mPreProcessedItem = true;
 #endif
 
-    if (!item->CanBeReused() || item->HasDeletedFrame()) {
+    if (item->HasDeletedFrame() || !item->CanBeReused()) {
       size_t i = aList->mOldItems.Length();
       aList->mOldItems.AppendElement(OldItemInfo(nullptr));
       item->Destroy(&mBuilder);
 
       if (initializeDAG) {
         if (i == 0) {
           aList->mDAG.AddNode(Span<const MergedListIndex>());
         } else {
@@ -208,32 +209,27 @@ void RetainedDisplayListBuilder::Increme
   MOZ_ASSERT(subDocFrame);
 
   nsIPresShell* presShell = subDocFrame->GetSubdocumentPresShellForPainting(0);
   MOZ_ASSERT(presShell);
 
   mBuilder.IncrementPresShellPaintCount(presShell);
 }
 
-bool AnyContentAncestorModified(nsIFrame* aFrame, nsIFrame* aStopAtFrame) {
-  nsIFrame* f = aFrame;
-  while (f) {
+static bool AnyContentAncestorModified(nsIFrame* aFrame,
+                                       nsIFrame* aStopAtFrame = nullptr) {
+  for (nsIFrame* f = aFrame; f;
+       f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
     if (f->IsFrameModified()) {
       return true;
     }
 
     if (aStopAtFrame && f == aStopAtFrame) {
       break;
     }
-
-    if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) {
-      f = f->GetParent();
-    } else {
-      f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f);
-    }
   }
 
   return false;
 }
 
 static Maybe<const ActiveScrolledRoot*> SelectContainerASR(
     const DisplayItemClipChain* aClipChain, const ActiveScrolledRoot* aItemASR,
     Maybe<const ActiveScrolledRoot*>& aContainerASR) {
@@ -274,24 +270,16 @@ static void UpdateASR(nsDisplayItem* aIt
   }
 
   wrapList->SetActiveScrolledRoot(ActiveScrolledRoot::PickAncestor(
       wrapList->GetFrameActiveScrolledRoot(), *asr));
 
   wrapList->UpdateHitTestInfoActiveScrolledRoot(*asr);
 }
 
-OldItemInfo::OldItemInfo(nsDisplayItem* aItem)
-    : mItem(aItem), mUsed(false), mDiscarded(false) {
-  if (mItem) {
-    // Clear cached modified frame state when adding an item to the old list.
-    mItem->SetModifiedFrame(false);
-  }
-}
-
 void OldItemInfo::AddedMatchToMergedList(RetainedDisplayListBuilder* aBuilder,
                                          MergedListIndex aIndex) {
   AddedToMergedList(aIndex);
 }
 
 void OldItemInfo::Discard(RetainedDisplayListBuilder* aBuilder,
                           nsTArray<MergedListIndex>&& aDirectPredecessors) {
   MOZ_ASSERT(!IsUsed());
@@ -299,50 +287,48 @@ void OldItemInfo::Discard(RetainedDispla
   mDirectPredecessors = std::move(aDirectPredecessors);
   if (mItem) {
     mItem->Destroy(aBuilder->Builder());
   }
   mItem = nullptr;
 }
 
 bool OldItemInfo::IsChanged() {
-  return !mItem || !mItem->CanBeReused() || mItem->HasDeletedFrame();
+  return !mItem || mItem->HasDeletedFrame() || !mItem->CanBeReused();
 }
 
 /**
  * A C++ implementation of Markus Stange's merge-dags algorithm.
  * https://github.com/mstange/merge-dags
  *
  * MergeState handles combining a new list of display items into an existing
  * DAG and computes the new DAG in a single pass.
  * Each time we add a new item, we resolve all dependencies for it, so that the
  * resulting list and DAG are built in topological ordering.
  */
 class MergeState {
  public:
   MergeState(RetainedDisplayListBuilder* aBuilder,
-             RetainedDisplayList& aOldList, nsDisplayItem* aOuterItem)
+             RetainedDisplayList& aOldList, uint32_t aOuterKey)
       : mBuilder(aBuilder),
         mOldList(&aOldList),
         mOldItems(std::move(aOldList.mOldItems)),
         mOldDAG(
             std::move(*reinterpret_cast<DirectedAcyclicGraph<OldListUnits>*>(
                 &aOldList.mDAG))),
-        mOuterItem(aOuterItem),
+        mOuterKey(aOuterKey),
         mResultIsModified(false) {
     mMergedDAG.EnsureCapacityFor(mOldDAG);
     MOZ_RELEASE_ASSERT(mOldItems.Length() == mOldDAG.Length());
   }
 
   Maybe<MergedListIndex> ProcessItemFromNewList(
       nsDisplayItem* aNewItem, const Maybe<MergedListIndex>& aPreviousItem) {
     OldListIndex oldIndex;
-    MOZ_DIAGNOSTIC_ASSERT(aNewItem->HasModifiedFrame() ==
-                          HasModifiedFrame(aNewItem));
-    if (!aNewItem->HasModifiedFrame() &&
+    if (!HasModifiedFrame(aNewItem) &&
         HasMatchingItemInOldList(aNewItem, &oldIndex)) {
       nsDisplayItem* oldItem = mOldItems[oldIndex.val].mItem;
       MOZ_DIAGNOSTIC_ASSERT(oldItem->GetPerFrameKey() ==
                                 aNewItem->GetPerFrameKey() &&
                             oldItem->Frame() == aNewItem->Frame());
       if (!mOldItems[oldIndex.val].IsChanged()) {
         MOZ_DIAGNOSTIC_ASSERT(!mOldItems[oldIndex.val].IsUsed());
         nsDisplayItem* destItem;
@@ -472,31 +458,29 @@ class MergeState {
     return result;
   }
 
   bool HasMatchingItemInOldList(nsDisplayItem* aItem, OldListIndex* aOutIndex) {
     nsIFrame::DisplayItemArray* items =
         aItem->Frame()->GetProperty(nsIFrame::DisplayItems());
     // Look for an item that matches aItem's frame and per-frame-key, but isn't
     // the same item.
-    uint32_t outerKey = mOuterItem ? mOuterItem->GetPerFrameKey() : 0;
     for (nsDisplayItem* i : *items) {
       if (i != aItem && i->Frame() == aItem->Frame() &&
           i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
-        if (i->GetOldListIndex(mOldList, outerKey, aOutIndex)) {
+        if (i->GetOldListIndex(mOldList, mOuterKey, aOutIndex)) {
           return true;
         }
       }
     }
     return false;
   }
 
   bool HasModifiedFrame(nsDisplayItem* aItem) {
-    nsIFrame* stopFrame = mOuterItem ? mOuterItem->Frame() : nullptr;
-    return AnyContentAncestorModified(aItem->FrameForInvalidation(), stopFrame);
+    return AnyContentAncestorModified(aItem->FrameForInvalidation());
   }
 
   void UpdateContainerASR(nsDisplayItem* aItem) {
     mContainerASR = SelectContainerASR(
         aItem->GetClipChain(), aItem->GetActiveScrolledRoot(), mContainerASR);
   }
 
   MergedListIndex AddNewNode(
@@ -507,21 +491,22 @@ class MergeState {
     aItem->NotifyUsed(mBuilder->Builder());
 
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
     nsIFrame::DisplayItemArray* items =
         aItem->Frame()->GetProperty(nsIFrame::DisplayItems());
     for (nsDisplayItem* i : *items) {
       if (i->Frame() == aItem->Frame() &&
           i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
-        MOZ_DIAGNOSTIC_ASSERT(!i->IsMergedItem());
+        MOZ_DIAGNOSTIC_ASSERT(!i->mMergedItem);
       }
     }
 
-    aItem->SetMergedPreProcessed(true, false);
+    aItem->mMergedItem = true;
+    aItem->mPreProcessedItem = false;
 #endif
 
     mMergedItems.AppendToTop(aItem);
     MergedListIndex newIndex =
         mMergedDAG.AddNode(aDirectPredecessors, aExtraDirectPredecessor);
     return newIndex;
   }
 
@@ -629,17 +614,17 @@ class MergeState {
   Maybe<const ActiveScrolledRoot*> mContainerASR;
   nsTArray<OldItemInfo> mOldItems;
   DirectedAcyclicGraph<OldListUnits> mOldDAG;
   // Unfortunately we can't use strong typing for the hashtables
   // since they internally encode the type with the mOps pointer,
   // and assert when we try swap the contents
   nsDisplayList mMergedItems;
   DirectedAcyclicGraph<MergedListUnits> mMergedDAG;
-  nsDisplayItem* mOuterItem;
+  uint32_t mOuterKey;
   bool mResultIsModified;
 };
 
 /**
  * Takes two display lists and merges them into an output list.
  *
  * Display lists wthout an explicit DAG are interpreted as linear DAGs (with a
  * maximum of one direct predecessor and one direct successor per node). We add
@@ -651,17 +636,18 @@ class MergeState {
  */
 bool RetainedDisplayListBuilder::MergeDisplayLists(
     nsDisplayList* aNewList, RetainedDisplayList* aOldList,
     RetainedDisplayList* aOutList,
     mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR,
     nsDisplayItem* aOuterItem) {
   AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_DisplayListMerging);
 
-  MergeState merge(this, *aOldList, aOuterItem);
+  MergeState merge(this, *aOldList,
+                   aOuterItem ? aOuterItem->GetPerFrameKey() : 0);
 
   Maybe<MergedListIndex> previousItemIndex;
   while (nsDisplayItem* item = aNewList->RemoveBottom()) {
     previousItemIndex = merge.ProcessItemFromNewList(item, previousItemIndex);
   }
 
   *aOutList = merge.Finalize();
   aOutContainerASR = merge.mContainerASR;
--- a/layout/painting/RetainedDisplayListHelpers.h
+++ b/layout/painting/RetainedDisplayListHelpers.h
@@ -134,17 +134,18 @@ class DirectedAcyclicGraph {
   nsTArray<NodeInfo> mNodesInfo;
   nsTArray<Index<T>> mDirectPredecessorList;
 };
 
 struct RetainedDisplayListBuilder;
 class nsDisplayItem;
 
 struct OldItemInfo {
-  explicit OldItemInfo(nsDisplayItem* aItem);
+  explicit OldItemInfo(nsDisplayItem* aItem)
+      : mItem(aItem), mUsed(false), mDiscarded(false) {}
 
   void AddedToMergedList(MergedListIndex aIndex) {
     MOZ_ASSERT(!IsUsed());
     mUsed = true;
     mIndex = aIndex;
     mItem = nullptr;
   }
 
@@ -163,12 +164,9 @@ struct OldItemInfo {
 
   nsDisplayItem* mItem;
   bool mUsed;
   bool mDiscarded;
   MergedListIndex mIndex;
   nsTArray<MergedListIndex> mDirectPredecessors;
 };
 
-bool AnyContentAncestorModified(nsIFrame* aFrame,
-                                nsIFrame* aStopAtFrame = nullptr);
-
 #endif  // RETAINEDDISPLAYLISTHELPERS_H_
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -96,17 +96,16 @@
 #include "nsSliderFrame.h"
 #include "ClientLayerManager.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layers/WebRenderMessages.h"
 #include "mozilla/layers/WebRenderScrollData.h"
-#include "mozilla/layout/RenderFrame.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::layout;
 using namespace mozilla::gfx;
 
 typedef ScrollableLayerGuid::ViewID ViewID;
@@ -131,17 +130,17 @@ void AssertUniqueItem(nsDisplayItem* aIt
   nsIFrame::DisplayItemArray* items =
       aItem->Frame()->GetProperty(nsIFrame::DisplayItems());
   if (!items) {
     return;
   }
   for (nsDisplayItem* i : *items) {
     if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
         i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
-      if (i->IsPreProcessedItem()) {
+      if (i->mPreProcessedItem) {
         continue;
       }
       MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
     }
   }
 }
 #endif
 
@@ -3167,19 +3166,28 @@ static_assert(sizeof(nsDisplayItem) <= 1
 #endif
 
 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
     : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {}
 
 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                              const ActiveScrolledRoot* aActiveScrolledRoot)
     : mFrame(aFrame),
-      mItemFlags(),
       mActiveScrolledRoot(aActiveScrolledRoot),
-      mAnimatedGeometryRoot(nullptr) {
+      mAnimatedGeometryRoot(nullptr),
+      mForceNotVisible(aBuilder->IsBuildingInvisibleItems()),
+      mDisableSubpixelAA(false),
+      mReusedItem(false),
+      mPaintRectValid(false),
+      mCanBeReused(true)
+#ifdef MOZ_DUMP_PAINTING
+      ,
+      mPainted(false)
+#endif
+{
   MOZ_COUNT_CTOR(nsDisplayItem);
   if (aBuilder->IsRetainingDisplayList()) {
     mFrame->AddDisplayItem(this);
   }
   mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
   // This can return the wrong result if the item override
   // ShouldFixToViewport(), the item needs to set it again in its constructor.
   mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(aFrame);
@@ -3195,66 +3203,45 @@ nsDisplayItem::nsDisplayItem(nsDisplayLi
 
   // The visible rect is for mCurrentFrame, so we have to use
   // mCurrentOffsetToReferenceFrame
   nsRect visible = aBuilder->GetVisibleRect() +
                    aBuilder->GetCurrentFrameOffsetToReferenceFrame();
   SetBuildingRect(visible);
 
   const nsStyleDisplay* disp = mFrame->StyleDisplay();
-  if (mFrame->BackfaceIsHidden(disp)) {
-    mItemFlags += ItemFlag::BackfaceHidden;
-  }
-  if (mFrame->Combines3DTransformWithAncestors(disp)) {
-    mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
-  }
+  mBackfaceIsHidden = mFrame->BackfaceIsHidden(disp);
+  mCombines3DTransformWithAncestors =
+      mFrame->Combines3DTransformWithAncestors(disp);
 }
 
 /* static */
 bool nsDisplayItem::ForceActiveLayers() {
   static bool sForce = false;
   static bool sForceCached = false;
 
   if (!sForceCached) {
     Preferences::AddBoolVarCache(&sForce, "layers.force-active", false);
     sForceCached = true;
   }
 
   return sForce;
 }
 
-bool nsDisplayItem::HasModifiedFrame() const {
-  return mItemFlags.contains(ItemFlag::ModifiedFrame);
-}
-
-void nsDisplayItem::SetModifiedFrame(bool aModified) {
-  if (aModified) {
-    mItemFlags += ItemFlag::ModifiedFrame;
-  } else {
-    mItemFlags -= ItemFlag::ModifiedFrame;
-  }
-}
-
-bool nsDisplayItem::HasDeletedFrame() const {
-  return mItemFlags.contains(ItemFlag::DeletedFrame) ||
-         (GetType() == DisplayItemType::TYPE_REMOTE &&
-          !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader());
-}
-
 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex(); }
 
 bool nsDisplayItem::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                       nsRegion* aVisibleRegion) {
   return !GetPaintRect().IsEmpty() &&
          !IsInvisibleInRect(aVisibleRegion->GetBounds());
 }
 
 bool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
                                         nsRegion* aVisibleRegion) {
-  if (ForceNotVisible() && !GetSameCoordinateSystemChildren()) {
+  if (mForceNotVisible && !GetSameCoordinateSystemChildren()) {
     // mForceNotVisible wants to ensure that this display item doesn't render
     // anything itself. If this item has contents, then we obviously want to
     // render those, so we don't need this check in that case.
     NS_ASSERTION(GetBuildingRect().IsEmpty(),
                  "invisible items without children should have empty vis rect");
     SetPaintRect(nsRect());
   } else {
     bool snap;
@@ -3309,18 +3296,16 @@ void nsDisplayItem::FuseClipChainUpTo(ns
 
   if (mClipChain) {
     mClip = &mClipChain->mClip;
   } else {
     mClip = nullptr;
   }
 }
 
-void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
-
 bool nsDisplayItem::ShouldUseAdvancedLayer(LayerManager* aManager,
                                            PrefFunc aFunc) const {
   return CanUseAdvancedLayer(aManager) ? aFunc() : false;
 }
 
 bool nsDisplayItem::CanUseAdvancedLayer(LayerManager* aManager) const {
   return gfxPrefs::LayersAdvancedBasicLayerEnabled() || !aManager ||
          aManager->GetBackendType() == layers::LayersBackend::LAYERS_WR;
@@ -6708,20 +6693,23 @@ nsDisplaySubDocument::~nsDisplaySubDocum
     mSubDocFrame->RemoveDisplayItem(this);
   }
 }
 
 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const {
   return mSubDocFrame ? mSubDocFrame : mFrame;
 }
 
+bool nsDisplaySubDocument::HasDeletedFrame() const {
+  return !mSubDocFrame || nsDisplayItem::HasDeletedFrame();
+}
+
 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) {
   if (aFrame == mSubDocFrame) {
     mSubDocFrame = nullptr;
-    SetDeletedFrame();
   }
   nsDisplayItem::RemoveFrame(aFrame);
 }
 
 void nsDisplaySubDocument::Disown() {
   if (mFrame) {
     mFrame->RemoveDisplayItem(this);
     mFrame = nullptr;
@@ -8951,17 +8939,17 @@ bool nsDisplayText::CanApplyOpacity() co
 
   return true;
 }
 
 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
   AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
 
   DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
-                                                    IsSubpixelAADisabled());
+                                                    mDisableSubpixelAA);
   RenderToContext(aCtx, aBuilder);
 }
 
 bool nsDisplayText::CreateWebRenderCommands(
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
@@ -10279,91 +10267,8 @@ PaintTelemetry::AutoRecord::~AutoRecord(
   if (mStart.IsNull()) {
     return;
   }
 
   sMetrics[mMetric] += (TimeStamp::Now() - mStart).ToMilliseconds();
 }
 
 }  // namespace mozilla
-
-#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) {
-  if (aFrame->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) {
-    return aFrame;
-  }
-
-  if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
-      !aFrame->GetPrevInFlow()) {
-    return aFrame->GetPlaceholderFrame();
-  }
-
-  return aFrame;
-}
-
-static nsIFrame* GetAncestorFor(nsIFrame* aFrame) {
-  nsIFrame* f = GetSelfOrPlaceholderFor(aFrame);
-  MOZ_ASSERT(f);
-  return nsLayoutUtils::GetCrossDocParentFrame(f);
-}
-#endif
-
-nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList(
-    nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
-    const nsRect& aVisibleRect, const nsRect& aDirtyRect,
-    const bool aIsTransformed, RecalcInInvalidSubtree aRecalcInvalidSubtree)
-    : mBuilder(aBuilder),
-      mPrevFrame(aBuilder->mCurrentFrame),
-      mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
-      mPrevHitTestArea(aBuilder->mHitTestArea),
-      mPrevHitTestInfo(aBuilder->mHitTestInfo),
-      mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
-      mPrevVisibleRect(aBuilder->mVisibleRect),
-      mPrevDirtyRect(aBuilder->mDirtyRect),
-      mPrevAGR(aBuilder->mCurrentAGR),
-      mPrevAncestorHasApzAwareEventHandler(
-          aBuilder->mAncestorHasApzAwareEventHandler),
-      mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
-      mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
-#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-  // Validate that aForChild is being visited from it's parent frame if
-  // recalculation of mInInvalidSubtree isn't requested.
-  const nsIFrame* ancestor = GetAncestorFor(aForChild);
-  MOZ_DIAGNOSTIC_ASSERT(aRecalcInvalidSubtree ==
-                            nsDisplayListBuilder::RIIS_YES ||
-                        aForChild == mPrevFrame || ancestor == mPrevFrame);
-#endif
-
-  if (aIsTransformed) {
-    aBuilder->mCurrentOffsetToReferenceFrame = nsPoint();
-    aBuilder->mCurrentReferenceFrame = aForChild;
-  } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
-    aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
-  } else {
-    aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
-        aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
-  }
-
-  bool isAsync;
-  mCurrentAGRState = aBuilder->IsAnimatedGeometryRoot(aForChild, isAsync);
-
-  if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
-    if (mCurrentAGRState == AGR_YES) {
-      aBuilder->mCurrentAGR =
-          aBuilder->WrapAGRForFrame(aForChild, isAsync, aBuilder->mCurrentAGR);
-    }
-  } else if (aBuilder->mCurrentFrame != aForChild) {
-    aBuilder->mCurrentAGR = aBuilder->FindAnimatedGeometryRootFor(aForChild);
-  }
-
-  MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(
-      aBuilder->RootReferenceFrame(), *aBuilder->mCurrentAGR));
-  if (!aRecalcInvalidSubtree) {
-    aBuilder->mInInvalidSubtree = aBuilder->mInInvalidSubtree ||
-      aForChild->IsFrameModified();
-  } else {
-    aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild);
-  }
-  aBuilder->mCurrentFrame = aForChild;
-  aBuilder->mVisibleRect = aVisibleRect;
-  aBuilder->mDirtyRect =
-      aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
-}
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -437,21 +437,16 @@ class nsDisplayListBuilder {
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
   typedef mozilla::layers::ScrollableLayerGuid::ViewID ViewID;
   typedef mozilla::gfx::CompositorHitTestInfo CompositorHitTestInfo;
   typedef mozilla::gfx::Matrix4x4 Matrix4x4;
   typedef mozilla::Maybe<mozilla::layers::ScrollDirection> MaybeScrollDirection;
 
   /**
-   * Does InInvalidSubtree need to recalculated?
-   */
-  enum RecalcInInvalidSubtree { RIIS_NO, RIIS_YES };
-
-  /**
    * @param aReferenceFrame the frame at the root of the subtree; its origin
    * is the origin of the reference coordinate system for this display list
    * @param aMode encodes what the builder is being used for.
    * @param aBuildCaret whether or not we should include the caret in any
    * display lists that we make.
    */
   nsDisplayListBuilder(nsIFrame* aReferenceFrame,
                        nsDisplayListBuilderMode aMode, bool aBuildCaret,
@@ -1132,35 +1127,69 @@ class nsDisplayListBuilder {
   /**
    * A helper class used to temporarily set nsDisplayListBuilder properties for
    * building display items.
    * aVisibleRect and aDirtyRect are relative to aForChild.
    */
   class AutoBuildingDisplayList {
    public:
     AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
-                            RecalcInInvalidSubtree aRecalcInvalidSubtree)
-        : AutoBuildingDisplayList(
-              aBuilder, aForChild, aBuilder->GetVisibleRect(),
-              aBuilder->GetDirtyRect(), aForChild->IsTransformed(),
-              aRecalcInvalidSubtree) {}
-
-    AutoBuildingDisplayList(
-        nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
-        const nsRect& aVisibleRect, const nsRect& aDirtyRect,
-        RecalcInInvalidSubtree aRecalcInvalidSubtree = RIIS_NO)
+                            const nsRect& aVisibleRect,
+                            const nsRect& aDirtyRect)
         : AutoBuildingDisplayList(aBuilder, aForChild, aVisibleRect, aDirtyRect,
-                                  aForChild->IsTransformed(),
-                                  aRecalcInvalidSubtree) {}
-
-    AutoBuildingDisplayList(
-        nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
-        const nsRect& aVisibleRect, const nsRect& aDirtyRect,
-        const bool aIsTransformed,
-        RecalcInInvalidSubtree aRecalcInvalidSubtree = RIIS_NO);
+                                  aForChild->IsTransformed()) {}
+
+    AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
+                            const nsRect& aVisibleRect,
+                            const nsRect& aDirtyRect, const bool aIsTransformed)
+        : mBuilder(aBuilder),
+          mPrevFrame(aBuilder->mCurrentFrame),
+          mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
+          mPrevHitTestArea(aBuilder->mHitTestArea),
+          mPrevHitTestInfo(aBuilder->mHitTestInfo),
+          mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
+          mPrevVisibleRect(aBuilder->mVisibleRect),
+          mPrevDirtyRect(aBuilder->mDirtyRect),
+          mPrevAGR(aBuilder->mCurrentAGR),
+          mPrevAncestorHasApzAwareEventHandler(
+              aBuilder->mAncestorHasApzAwareEventHandler),
+          mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems),
+          mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) {
+      if (aIsTransformed) {
+        aBuilder->mCurrentOffsetToReferenceFrame = nsPoint();
+        aBuilder->mCurrentReferenceFrame = aForChild;
+      } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
+        aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
+      } else {
+        aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor(
+            aForChild, &aBuilder->mCurrentOffsetToReferenceFrame);
+      }
+
+      bool isAsync;
+      mCurrentAGRState = aBuilder->IsAnimatedGeometryRoot(aForChild, isAsync);
+
+      if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
+        if (mCurrentAGRState == AGR_YES) {
+          aBuilder->mCurrentAGR = aBuilder->WrapAGRForFrame(
+              aForChild, isAsync, aBuilder->mCurrentAGR);
+        }
+      } else if (aBuilder->mCurrentFrame != aForChild) {
+        aBuilder->mCurrentAGR =
+            aBuilder->FindAnimatedGeometryRootFor(aForChild);
+      }
+
+      MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(
+          aBuilder->RootReferenceFrame(), *aBuilder->mCurrentAGR));
+      aBuilder->mInInvalidSubtree =
+          aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
+      aBuilder->mCurrentFrame = aForChild;
+      aBuilder->mVisibleRect = aVisibleRect;
+      aBuilder->mDirtyRect =
+          aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
+    }
 
     void SetReferenceFrameAndCurrentOffset(const nsIFrame* aFrame,
                                            const nsPoint& aOffset) {
       mBuilder->mCurrentReferenceFrame = aFrame;
       mBuilder->mCurrentOffsetToReferenceFrame = aOffset;
     }
 
     bool IsAnimatedGeometryRoot() const { return mCurrentAGRState == AGR_YES; }
@@ -1726,17 +1755,27 @@ class nsDisplayListBuilder {
     if (!aFrame->IsFrameModified()) {
       mModifiedFramesDuringBuilding.AppendElement(aFrame);
       aFrame->SetFrameIsModified(true);
       return true;
     }
     return false;
   }
 
+  bool MarkCurrentFrameModifiedDuringBuilding() {
+    if (MarkFrameModifiedDuringBuilding(const_cast<nsIFrame*>(mCurrentFrame))) {
+      mInInvalidSubtree = true;
+      mDirtyRect = mVisibleRect;
+      return true;
+    }
+    return false;
+  }
+
   void RebuildAllItemsInCurrentSubtree() {
+    mInInvalidSubtree = true;
     mDirtyRect = mVisibleRect;
   }
 
   /**
    * This is a convenience function to ease the transition until AGRs and ASRs
    * are unified.
    */
   AnimatedGeometryRoot* AnimatedGeometryRootForASR(
@@ -2067,32 +2106,21 @@ MOZ_ALWAYS_INLINE T* MakeDisplayItem(nsD
         if (!did->HasMergedFrames()) {
           item->SetDisplayItemData(did, did->GetLayer()->Manager());
         }
         break;
       }
     }
   }
 
-  if (aBuilder->InInvalidSubtree() ||
-      item->FrameForInvalidation()->IsFrameModified()) {
-    item->SetModifiedFrame(true);
-  }
-
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   if (aBuilder->IsRetainingDisplayList() && !aBuilder->IsInPageSequence() &&
       aBuilder->IsBuilding()) {
     AssertUniqueItem(item);
   }
-
-  // Verify that InInvalidSubtree matches invalidation frame's modified state.
-  if (aBuilder->InInvalidSubtree()) {
-    MOZ_DIAGNOSTIC_ASSERT(
-        AnyContentAncestorModified(item->FrameForInvalidation()));
-  }
 #endif
 
   return item;
 }
 
 /**
  * This is the unit of rendering and event testing. Each instance of this
  * class represents an entity that can be drawn on the screen, e.g., a
@@ -2150,24 +2178,23 @@ class nsDisplayItem : public nsDisplayIt
     DisplayItemType type = GetType();
     this->~nsDisplayItem();
     aBuilder->Destroy(type, this);
   }
 
   virtual void RestoreState() {
     mClipChain = mState.mClipChain;
     mClip = mState.mClip;
-    mItemFlags -= ItemFlag::DisableSubpixelAA;
+    mDisableSubpixelAA = false;
   }
 
   virtual void RemoveFrame(nsIFrame* aFrame) {
     if (mFrame && aFrame == mFrame) {
       MOZ_ASSERT(!mFrame->HasDisplayItem(this));
       mFrame = nullptr;
-      SetDeletedFrame();
       SetDisplayItemData(nullptr, nullptr);
     }
   }
 
   /**
    * Downcasts this item to nsDisplayWrapList, if possible.
    */
   virtual const nsDisplayWrapList* AsDisplayWrapList() const { return nullptr; }
@@ -2183,39 +2210,38 @@ class nsDisplayItem : public nsDisplayIt
   nsDisplayItem(const nsDisplayItem&) = delete;
   /**
    * The custom copy-constructor is implemented to prevent copying the saved
    * state of the item.
    * This is currently only used when creating temporary items for merging.
    */
   nsDisplayItem(nsDisplayListBuilder* aBuilder, const nsDisplayItem& aOther)
       : mFrame(aOther.mFrame),
-        mItemFlags(),
         mClipChain(aOther.mClipChain),
         mClip(aOther.mClip),
         mActiveScrolledRoot(aOther.mActiveScrolledRoot),
         mReferenceFrame(aOther.mReferenceFrame),
         mAnimatedGeometryRoot(aOther.mAnimatedGeometryRoot),
         mToReferenceFrame(aOther.mToReferenceFrame),
         mBuildingRect(aOther.mBuildingRect),
-        mPaintRect(aOther.mPaintRect) {
+        mPaintRect(aOther.mPaintRect),
+        mForceNotVisible(aOther.mForceNotVisible),
+        mDisableSubpixelAA(aOther.mDisableSubpixelAA),
+        mReusedItem(false),
+        mBackfaceIsHidden(aOther.mBackfaceIsHidden),
+        mCombines3DTransformWithAncestors(
+            aOther.mCombines3DTransformWithAncestors),
+        mPaintRectValid(false),
+        mCanBeReused(true)
+#ifdef MOZ_DUMP_PAINTING
+        ,
+        mPainted(false)
+#endif
+  {
     MOZ_COUNT_CTOR(nsDisplayItem);
-    // TODO: It might be better to remove the flags that aren't copied.
-    if (aOther.ForceNotVisible()) {
-      mItemFlags += ItemFlag::ForceNotVisible;
-    }
-    if (aOther.IsSubpixelAADisabled()) {
-      mItemFlags += ItemFlag::DisableSubpixelAA;
-    }
-    if (mFrame->In3DContextAndBackfaceIsHidden()) {
-      mItemFlags += ItemFlag::BackfaceHidden;
-    }
-    if (aOther.Combines3DTransformWithAncestors()) {
-      mItemFlags += ItemFlag::Combines3DTransformWithAncestors;
-    }
   }
 
   struct HitTestState {
     explicit HitTestState() : mInPreserves3D(false) {}
 
     ~HitTestState() {
       NS_ASSERTION(mItemBuffer.Length() == 0,
                    "mItemBuffer should have been cleared");
@@ -2269,20 +2295,17 @@ class nsDisplayItem : public nsDisplayIt
   }
 
   /**
    * @return the nsIFrame that provides the style data, and should
    * be checked when deciding if this display item can be reused.
    */
   virtual nsIFrame* FrameForInvalidation() const { return mFrame; }
 
-  bool HasModifiedFrame() const;
-  void SetModifiedFrame(bool aModified);
-
-  bool HasDeletedFrame() const;
+  virtual bool HasDeletedFrame() const { return !mFrame; }
 
   virtual nsIFrame* StyleFrame() const { return mFrame; }
 
   /**
    * Compute the used z-index of our frame; returns zero for elements to which
    * z-index does not apply, and for z-index:auto.
    * @note This can be overridden, @see nsDisplayWrapList::SetOverrideZIndex.
    */
@@ -2516,22 +2539,22 @@ class nsDisplayItem : public nsDisplayIt
   virtual void PaintWithClip(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
                              const DisplayItemClip& aClip) {}
 
 #ifdef MOZ_DUMP_PAINTING
   /**
    * Mark this display item as being painted via
    * FrameLayerBuilder::DrawPaintedLayer.
    */
-  bool Painted() const { return mItemFlags.contains(ItemFlag::Painted); }
+  bool Painted() const { return mPainted; }
 
   /**
    * Check if this display item has been painted.
    */
-  void SetPainted() { mItemFlags += ItemFlag::Painted; }
+  void SetPainted() { mPainted = true; }
 #endif
 
   /**
    * Get the layer drawn by this display item. Call this only if
    * GetLayerState() returns something other than LAYER_NONE.
    * If GetLayerState returned LAYER_NONE then Paint will be called
    * instead.
    * This is called while aManager is in the construction phase.
@@ -2704,26 +2727,24 @@ class nsDisplayItem : public nsDisplayIt
 
   void SetBuildingRect(const nsRect& aBuildingRect) {
     if (aBuildingRect == mBuildingRect) {
       // Avoid unnecessary paint rect recompution when the
       // building rect is staying the same.
       return;
     }
     mPaintRect = mBuildingRect = aBuildingRect;
-    mItemFlags -= ItemFlag::PaintRectValid;
+    mPaintRectValid = false;
   }
 
   void SetPaintRect(const nsRect& aPaintRect) {
     mPaintRect = aPaintRect;
-    mItemFlags += ItemFlag::PaintRectValid;
-  }
-  bool HasPaintRect() const {
-    return mItemFlags.contains(ItemFlag::PaintRectValid);
-  }
+    mPaintRectValid = true;
+  }
+  bool HasPaintRect() const { return mPaintRectValid; }
 
   /**
    * Returns the building rect for the children, relative to their
    * reference frame. Can be different from mBuildingRect for
    * nsDisplayTransform, since the reference frame for the children is different
    * from the reference frame for the item itself.
    */
   virtual const nsRect& GetBuildingRectForChildren() const {
@@ -2739,19 +2760,17 @@ class nsDisplayItem : public nsDisplayIt
     NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity not supported on this type");
   }
   /**
    * Returns true if this display item would return true from ApplyOpacity
    * without actually applying the opacity. Otherwise returns false.
    */
   virtual bool CanApplyOpacity() const { return false; }
 
-  bool ForceNotVisible() const {
-    return mItemFlags.contains(ItemFlag::ForceNotVisible);
-  }
+  bool ForceNotVisible() const { return mForceNotVisible; }
 
   /**
    * For debugging and stuff
    */
   virtual const char* Name() const = 0;
 
   virtual void WriteDebugInfo(std::stringstream& aStream) {}
 
@@ -2807,21 +2826,19 @@ class nsDisplayItem : public nsDisplayIt
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const {
     return nsRect();
   }
 
   /**
    * Disable usage of component alpha. Currently only relevant for items that
    * have text.
    */
-  void DisableComponentAlpha() { mItemFlags += ItemFlag::DisableSubpixelAA; }
-
-  bool IsSubpixelAADisabled() const {
-    return mItemFlags.contains(ItemFlag::DisableSubpixelAA);
-  }
+  void DisableComponentAlpha() { mDisableSubpixelAA = true; }
+
+  bool IsSubpixelAADisabled() const { return mDisableSubpixelAA; }
 
   /**
    * Check if we can add async animations to the layer for this display item.
    */
   virtual bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) {
     return false;
   }
 
@@ -2847,58 +2864,47 @@ class nsDisplayItem : public nsDisplayIt
 
   /**
    * Intersect all clips in our clip chain up to (and including) aASR and set
    * set the intersection as this item's clip.
    */
   void FuseClipChainUpTo(nsDisplayListBuilder* aBuilder,
                          const ActiveScrolledRoot* aASR);
 
-  bool BackfaceIsHidden() const {
-    return mItemFlags.contains(ItemFlag::BackfaceHidden);
-  }
+  bool BackfaceIsHidden() const { return mBackfaceIsHidden; }
 
   bool Combines3DTransformWithAncestors() const {
-    return mItemFlags.contains(ItemFlag::Combines3DTransformWithAncestors);
+    return mCombines3DTransformWithAncestors;
   }
 
   bool In3DContextAndBackfaceIsHidden() const {
-    return mItemFlags.contains(ItemFlag::BackfaceHidden) &&
-           mItemFlags.contains(ItemFlag::Combines3DTransformWithAncestors);
+    return mBackfaceIsHidden && mCombines3DTransformWithAncestors;
   }
 
   bool HasDifferentFrame(const nsDisplayItem* aOther) const {
     return mFrame != aOther->mFrame;
   }
 
   bool HasSameTypeAndClip(const nsDisplayItem* aOther) const {
     return GetPerFrameKey() == aOther->GetPerFrameKey() &&
            GetClipChain() == aOther->GetClipChain();
   }
 
   bool HasSameContent(const nsDisplayItem* aOther) const {
     return mFrame->GetContent() == aOther->Frame()->GetContent();
   }
 
-  bool IsReused() const { return mItemFlags.contains(ItemFlag::ReusedItem); }
-  void SetReused(bool aReused) {
-    if (aReused) {
-      mItemFlags += ItemFlag::ReusedItem;
-    } else {
-      mItemFlags -= ItemFlag::ReusedItem;
-    }
-  }
-
-  bool CanBeReused() const {
-    return !mItemFlags.contains(ItemFlag::CantBeReused);
-  }
-  void SetCantBeReused() { mItemFlags += ItemFlag::CantBeReused; }
+  bool IsReused() const { return mReusedItem; }
+
+  void SetReused(bool aReused) { mReusedItem = aReused; }
+
+  bool CanBeReused() const { return mCanBeReused; }
   void DiscardIfOldItem() {
     if (mOldList) {
-      SetCantBeReused();
+      mCanBeReused = false;
     }
   }
   virtual void NotifyUsed(nsDisplayListBuilder* aBuilder) {}
 
   virtual nsIFrame* GetDependentFrame() { return nullptr; }
 
   virtual mozilla::Maybe<nsRect> GetClipWithRespectToASR(
       nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const;
@@ -2960,44 +2966,21 @@ class nsDisplayItem : public nsDisplayIt
 
   virtual bool HasHitTestInfo() const { return false; }
 
 #ifdef DEBUG
   virtual bool IsHitTestItem() const { return false; }
 #endif
 
  protected:
-  void SetDeletedFrame();
-
   typedef bool (*PrefFunc)(void);
   bool ShouldUseAdvancedLayer(LayerManager* aManager, PrefFunc aFunc) const;
   bool CanUseAdvancedLayer(LayerManager* aManager) const;
 
-  enum class ItemFlag {
-    ModifiedFrame,
-    DeletedFrame,
-    ForceNotVisible,
-    DisableSubpixelAA,
-    CantBeReused,
-    ReusedItem,
-    BackfaceHidden,
-    Combines3DTransformWithAncestors,
-    PaintRectValid,
-#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-    MergedItem,
-    PreProcessedItem,
-#endif
-#ifdef MOZ_DUMP_PAINTING
-    // True if this frame has been painted.
-    Painted,
-#endif
-  };
-
   nsIFrame* mFrame;
-  mozilla::EnumSet<ItemFlag, uint16_t> mItemFlags;
   RefPtr<const DisplayItemClipChain> mClipChain;
   const DisplayItemClip* mClip;
   RefPtr<const ActiveScrolledRoot> mActiveScrolledRoot;
   // Result of FindReferenceFrameFor(mFrame), if mFrame is non-null
   const nsIFrame* mReferenceFrame;
   RefPtr<struct AnimatedGeometryRoot> mAnimatedGeometryRoot;
   // Result of ToReferenceFrame(mFrame), if mFrame is non-null
   nsPoint mToReferenceFrame;
@@ -3021,41 +3004,36 @@ class nsDisplayItem : public nsDisplayIt
  protected:
   struct {
     RefPtr<const DisplayItemClipChain> mClipChain;
     const DisplayItemClip* mClip;
   } mState;
 
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  public:
-  bool IsMergedItem() const {
-    return mItemFlags.contains(ItemFlag::MergedItem);
-  }
-  bool IsPreProcessedItem() const {
-    return mItemFlags.contains(ItemFlag::PreProcessedItem);
-  }
-  void SetMergedPreProcessed(bool aMerged, bool aPreProcessed) {
-    if (aMerged) {
-      mItemFlags += ItemFlag::MergedItem;
-    } else {
-      mItemFlags -= ItemFlag::MergedItem;
-    }
-
-    if (aPreProcessed) {
-      mItemFlags += ItemFlag::PreProcessedItem;
-    } else {
-      mItemFlags -= ItemFlag::PreProcessedItem;
-    }
-  }
-
   uint32_t mOldListKey = 0;
   uint32_t mOldNestingDepth = 0;
+  bool mMergedItem = false;
+  bool mPreProcessedItem = false;
 
  protected:
 #endif
+
+  bool mForceNotVisible;
+  bool mDisableSubpixelAA;
+  bool mReusedItem;
+  bool mBackfaceIsHidden;
+  bool mCombines3DTransformWithAncestors;
+  bool mPaintRectValid;
+  bool mCanBeReused;
+
+#ifdef MOZ_DUMP_PAINTING
+  // True if this frame has been painted.
+  bool mPainted;
+#endif
 };
 
 /**
  * Manages a singly-linked list of display list items.
  *
  * mSentinel is the sentinel list value, the first value in the null-terminated
  * linked list of items. mTop is the last item in the list (whose 'above'
  * pointer is null). This class has no virtual methods. So list objects are just
@@ -4178,19 +4156,17 @@ class nsDisplaySolidColor : public nsDis
  public:
   nsDisplaySolidColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       const nsRect& aBounds, nscolor aColor,
                       bool aCanBeReused = true)
       : nsDisplaySolidColorBase(aBuilder, aFrame, aColor), mBounds(aBounds) {
     NS_ASSERTION(NS_GET_A(aColor) > 0,
                  "Don't create invisible nsDisplaySolidColors!");
     MOZ_COUNT_CTOR(nsDisplaySolidColor);
-    if (!aCanBeReused) {
-      SetCantBeReused();
-    }
+    mCanBeReused = aCanBeReused;
   }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
   ~nsDisplaySolidColor() override { MOZ_COUNT_DTOR(nsDisplaySolidColor); }
 #endif
 
   NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR)
 
@@ -4518,20 +4494,23 @@ class nsDisplayTableBackgroundImage : pu
            (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
            nsDisplayItem::GetPerFrameKey();
   }
 
   bool IsInvalid(nsRect& aRect) const override;
 
   nsIFrame* FrameForInvalidation() const override { return mStyleFrame; }
 
+  bool HasDeletedFrame() const override {
+    return !mStyleFrame || nsDisplayBackgroundImage::HasDeletedFrame();
+  }
+
   void RemoveFrame(nsIFrame* aFrame) override {
     if (aFrame == mStyleFrame) {
       mStyleFrame = nullptr;
-      SetDeletedFrame();
     }
     nsDisplayBackgroundImage::RemoveFrame(aFrame);
   }
 
  protected:
   nsIFrame* StyleFrame() const override { return mStyleFrame; }
 
   nsIFrame* mStyleFrame;
@@ -4646,20 +4625,23 @@ class nsDisplayTableThemedBackground : p
 
   uint32_t GetPerFrameKey() const override {
     return (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
            nsDisplayItem::GetPerFrameKey();
   }
 
   nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }
 
+  bool HasDeletedFrame() const override {
+    return !mAncestorFrame || nsDisplayThemedBackground::HasDeletedFrame();
+  }
+
   void RemoveFrame(nsIFrame* aFrame) override {
     if (aFrame == mAncestorFrame) {
       mAncestorFrame = nullptr;
-      SetDeletedFrame();
     }
     nsDisplayThemedBackground::RemoveFrame(aFrame);
   }
 
  protected:
   nsIFrame* StyleFrame() const override { return mAncestorFrame; }
   nsIFrame* mAncestorFrame;
   TableType mTableType;
@@ -4823,20 +4805,23 @@ class nsDisplayTableBackgroundColor : pu
       mAncestorFrame->RemoveDisplayItem(this);
     }
   }
 
   NS_DISPLAY_DECL_NAME("TableBackgroundColor", TYPE_TABLE_BACKGROUND_COLOR)
 
   nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }
 
+  bool HasDeletedFrame() const override {
+    return !mAncestorFrame || nsDisplayBackgroundColor::HasDeletedFrame();
+  }
+
   void RemoveFrame(nsIFrame* aFrame) override {
     if (aFrame == mAncestorFrame) {
       mAncestorFrame = nullptr;
-      SetDeletedFrame();
     }
     nsDisplayBackgroundColor::RemoveFrame(aFrame);
   }
 
   uint32_t GetPerFrameKey() const override {
     return (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
            nsDisplayItem::GetPerFrameKey();
   }
@@ -5642,20 +5627,23 @@ class nsDisplayTableBlendMode : public n
   NS_DISPLAY_DECL_NAME("TableBlendMode", TYPE_TABLE_BLEND_MODE)
 
   nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const override {
     return MakeDisplayItem<nsDisplayTableBlendMode>(aBuilder, *this);
   }
 
   nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }
 
+  bool HasDeletedFrame() const override {
+    return !mAncestorFrame || nsDisplayBlendMode::HasDeletedFrame();
+  }
+
   void RemoveFrame(nsIFrame* aFrame) override {
     if (aFrame == mAncestorFrame) {
       mAncestorFrame = nullptr;
-      SetDeletedFrame();
     }
     nsDisplayBlendMode::RemoveFrame(aFrame);
   }
 
   uint32_t GetPerFrameKey() const override {
     return (mIndex << (TYPE_BITS +
                        static_cast<uint8_t>(TableTypeBits::COUNT))) |
            (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
@@ -5746,20 +5734,23 @@ class nsDisplayTableBlendContainer : pub
   NS_DISPLAY_DECL_NAME("TableBlendContainer", TYPE_TABLE_BLEND_CONTAINER)
 
   nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const override {
     return MakeDisplayItem<nsDisplayTableBlendContainer>(aBuilder, *this);
   }
 
   nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }
 
+  bool HasDeletedFrame() const override {
+    return !mAncestorFrame || nsDisplayBlendContainer::HasDeletedFrame();
+  }
+
   void RemoveFrame(nsIFrame* aFrame) override {
     if (aFrame == mAncestorFrame) {
       mAncestorFrame = nullptr;
-      SetDeletedFrame();
     }
     nsDisplayBlendContainer::RemoveFrame(aFrame);
   }
 
   uint32_t GetPerFrameKey() const override {
     return (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
            nsDisplayItem::GetPerFrameKey();
   }
@@ -5976,16 +5967,17 @@ class nsDisplaySubDocument : public nsDi
   nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override;
 
   mozilla::UniquePtr<ScrollMetadata> ComputeScrollMetadata(
       LayerManager* aLayerManager,
       const ContainerLayerParameters& aContainerParameters);
 
   nsIFrame* FrameForInvalidation() const override;
+  bool HasDeletedFrame() const override;
   void RemoveFrame(nsIFrame* aFrame) override;
 
   void Disown();
 
  protected:
   ViewID mScrollParentId;
   bool mForceDispatchToContentRegion;
   bool mShouldFlatten;
@@ -6157,20 +6149,23 @@ class nsDisplayTableFixedPosition : publ
   NS_DISPLAY_DECL_NAME("TableFixedPosition", TYPE_TABLE_FIXED_POSITION)
 
   nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const override {
     return MakeDisplayItem<nsDisplayTableFixedPosition>(aBuilder, *this);
   }
 
   nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }
 
+  bool HasDeletedFrame() const override {
+    return !mAncestorFrame || nsDisplayFixedPosition::HasDeletedFrame();
+  }
+
   void RemoveFrame(nsIFrame* aFrame) override {
     if (aFrame == mAncestorFrame) {
       mAncestorFrame = nullptr;
-      SetDeletedFrame();
     }
     nsDisplayFixedPosition::RemoveFrame(aFrame);
   }
 
   uint32_t GetPerFrameKey() const override {
     return (mIndex << (TYPE_BITS +
                        static_cast<uint8_t>(TableTypeBits::COUNT))) |
            (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -2856,17 +2856,17 @@ void nsDisplaySVGText::HitTest(nsDisplay
   nsIFrame* target = frame->GetFrameForPoint(userSpacePt);
   if (target) {
     aOutFrames->AppendElement(target);
   }
 }
 
 void nsDisplaySVGText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
   DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
-                                                    IsSubpixelAADisabled());
+                                                    mDisableSubpixelAA);
 
   uint32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
 
   // ToReferenceFrame includes our mRect offset, but painting takes
   // account of that too. To avoid double counting, we subtract that
   // here.
   nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
 
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -273,17 +273,17 @@ static void PaintTextShadowCallback(gfxC
                                     const nscolor& aShadowColor, void* aData) {
   reinterpret_cast<nsDisplayXULTextBox*>(aData)->PaintTextToContext(
       aCtx, aShadowOffset, &aShadowColor);
 }
 
 void nsDisplayXULTextBox::Paint(nsDisplayListBuilder* aBuilder,
                                 gfxContext* aCtx) {
   DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
-                                                    IsSubpixelAADisabled());
+                                                    mDisableSubpixelAA);
 
   // Paint the text shadow before doing any foreground stuff
   nsRect drawRect =
       static_cast<nsTextBoxFrame*>(mFrame)->mTextDrawRect + ToReferenceFrame();
   nsLayoutUtils::PaintTextShadow(mFrame, aCtx, drawRect, GetPaintRect(),
                                  mFrame->StyleColor()->mColor.ToColor(),
                                  PaintTextShadowCallback, (void*)this);
 
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -2493,17 +2493,17 @@ class nsDisplayTreeBody final : public n
     nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry,
                                              aInvalidRegion);
   }
 
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      gfxContext* aCtx) override {
     MOZ_ASSERT(aBuilder);
     DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
-                                                      IsSubpixelAADisabled());
+                                                      mDisableSubpixelAA);
 
     ImgDrawResult result = static_cast<nsTreeBodyFrame*>(mFrame)->PaintTreeBody(
         *aCtx, GetPaintRect(), ToReferenceFrame(), aBuilder);
 
     nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
   }
 
   NS_DISPLAY_DECL_NAME("XULTreeBody", TYPE_XUL_TREE_BODY)
--- 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);
 };
--- a/remote/moz.build
+++ b/remote/moz.build
@@ -1,14 +1,16 @@
 # 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/.
 
-DIRS += ["pref"]
-TEST_DIRS += ["test"]
+DIRS += [
+  "pref",
+  "test",
+]
 
 EXTRA_COMPONENTS += [
     "command-line-handler.js",
     "RemoteAgent.manifest",
 ]
 
 JAR_MANIFESTS += ["jar.mn"]
 
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])
+