Merge mozilla-central to autoland. a=merge CLOSED TREE
authorshindli <shindli@mozilla.com>
Sat, 05 Jan 2019 06:13:47 +0200
changeset 509732 64ae1fbfe96463284ad62bc0bbf42e9828e7e21f
parent 509731 1ed041abb586e1085d1493403380d2e524b15c08 (current diff)
parent 509722 e0a4fe89a7b0a36b246f79121d7e4fd70bd898b7 (diff)
child 509733 689de183c7619741db70d37451e2cd2655909ba4
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.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 autoland. a=merge CLOSED TREE
dom/base/Document.cpp
dom/base/Document.h
modules/libpref/init/all.js
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -156,18 +156,18 @@ dependencies = [
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "baldrdash"
 version = "0.1.0"
 dependencies = [
  "bindgen 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-codegen 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-wasm 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "base64"
 version = "0.10.0"
@@ -348,16 +348,21 @@ name = "bzip2-sys"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "cast"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "cc"
 version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "cexpr"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -475,67 +480,68 @@ 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.25.0"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cranelift-entity 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.25.0"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cranelift-bforest 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-codegen-meta 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-entity 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-bforest 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-codegen-meta 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "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.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.25.0"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cranelift-entity 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.25.0"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.25.0"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cranelift-codegen 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-wasm"
-version = "0.25.0"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cranelift-codegen 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-entity 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-frontend 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cranelift-frontend 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "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.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "wasmparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "crc"
@@ -3194,37 +3200,38 @@ dependencies = [
 "checksum boxfnonce 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8380105befe91099e6f69206164072c05bc92427ff6aa8a5171388317346dd75"
 "checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9"
 "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
 "checksum byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "980479e6fde23246dfb54d47580d66b4e99202e7579c5eaa9fe10ecb5ebd2182"
 "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
 "checksum bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e178b8e0e239e844b083d5a0d4a156b2654e67f9f80144d48398fcd736a24fb8"
 "checksum bzip2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3eafc42c44e0d827de6b1c131175098fe7fb53b8ce8a47e65cb3ea94688be24"
 "checksum bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b"
+"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427"
 "checksum cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "c37f0efaa4b9b001fa6f02d4b644dee4af97d3414df07c51e3e4f015f3a3e131"
 "checksum cexpr 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fc0086be9ca82f7fc89fc873435531cb898b86e850005850de1f820e2db6e9b"
 "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
 "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
 "checksum clang-sys 0.26.1 (registry+https://github.com/rust-lang/crates.io-index)" = "481e42017c1416b1c0856ece45658ecbb7c93d8a93455f7e5fa77f3b35455557"
 "checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536"
 "checksum cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "56d741ea7a69e577f6d06b36b7dff4738f680593dc27a701ffa8506b73ce28bb"
 "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.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b2d527b241af388ff017d72f2b0b323929a70cf97342c6ec1534e3b0f4dfaa0"
-"checksum cranelift-codegen 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e92fa0fa287cf00a6739c46aba114957e0a8eeeb4f0d1aa65d6ed0699c34ca6b"
-"checksum cranelift-codegen-meta 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "963262697a05d9aa63ca40f4670a7243e4525f4a098e10d654c3f5143fcef686"
-"checksum cranelift-entity 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc9a0329208e5e0d7d4d6e64cd50985d4c4cbfdbeeb594ae2157a094b98e8dcc"
-"checksum cranelift-frontend 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98d9eb4a2343435d520499236c805725c88d6d55eefb9a6ad0819b7970c76bdd"
-"checksum cranelift-wasm 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5022a3a3d1044fdc8c97909b5e8d701884982dcfb43885034d004cfdd9b7d577"
+"checksum cranelift-bforest 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "40f8ff24e9a6c89b8a846b14df9a34d2cac17cea7bdb5c81ed6b4744ee0e38bf"
+"checksum cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "42f5b809bd885c368e01aeec8fe04f21dcb07569834b907d75b4a7bed8d067eb"
+"checksum cranelift-codegen-meta 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "014c23ed3ebdc8377d41540af638245207dd169f421df042dfccc867465734ed"
+"checksum cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4df40e26c0cf7b4d86919cb995bb412ee3001cc18e4f3c83a903f30b7007d8b"
+"checksum cranelift-frontend 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "789907218eeebebcea8122c2053d71affac91c96ce72cea35ebfdbbf547e82af"
+"checksum cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49723365dab9a48b354bdc24cb6d9d5719bc1d3b858ffd2ea179d0d7d885804a"
 "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 cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "730363a45c4e248d4f21d3e5c1156d1a9cdec0855056c0d9539e814bc59865c3"
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 2.1.145
+Current extension version is: 2.1.153
 
-Taken from upstream commit: d8f201ea
+Taken from upstream commit: 5a2bd9fc
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.1.145';
-var pdfjsBuild = 'd8f201ea';
+var pdfjsVersion = '2.1.153';
+var pdfjsBuild = '5a2bd9fc';
 
 var pdfjsSharedUtil = __w_pdfjs_require__(1);
 
 var pdfjsDisplayAPI = __w_pdfjs_require__(7);
 
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(19);
 
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(20);
@@ -5149,17 +5149,17 @@ function _fetchDocument(worker, source, 
 
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
 
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
     docId,
-    apiVersion: '2.1.145',
+    apiVersion: '2.1.153',
     source: {
       data: source.data,
       url: source.url,
       password: source.password,
       disableAutoFetch: source.disableAutoFetch,
       rangeChunkSize: source.rangeChunkSize,
       length: source.length
     },
@@ -6880,19 +6880,19 @@ const InternalRenderTask = function Inte
       }
     }
 
   }
 
   return InternalRenderTask;
 }();
 
-const version = '2.1.145';
+const version = '2.1.153';
 exports.version = version;
-const build = 'd8f201ea';
+const build = '5a2bd9fc';
 exports.build = build;
 
 /***/ }),
 /* 8 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
@@ -10973,22 +10973,28 @@ class SimpleDOMNode {
     this.nodeValue = nodeValue;
     Object.defineProperty(this, 'parentNode', {
       value: null,
       writable: true
     });
   }
 
   get firstChild() {
-    return this.childNodes[0];
+    return this.childNodes && this.childNodes[0];
   }
 
   get nextSibling() {
-    let index = this.parentNode.childNodes.indexOf(this);
-    return this.parentNode.childNodes[index + 1];
+    const childNodes = this.parentNode.childNodes;
+
+    if (!childNodes) {
+      return undefined;
+    }
+
+    const index = childNodes.indexOf(this);
+    return childNodes[index + 1];
   }
 
   get textContent() {
     if (!this.childNodes) {
       return this.nodeValue || '';
     }
 
     return this.childNodes.map(function (child) {
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.1.145';
-var pdfjsBuild = 'd8f201ea';
+var pdfjsVersion = '2.1.153';
+var pdfjsBuild = '5a2bd9fc';
 
 var pdfjsCoreWorker = __w_pdfjs_require__(1);
 
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
@@ -370,17 +370,17 @@ var WorkerMessageHandler = {
   },
 
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.1.145';
+    let workerVersion = '2.1.153';
 
     if (apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
     }
 
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
@@ -6942,18 +6942,18 @@ class PDFDocument {
         this.stream.ensureRange(0, Math.min(FINGERPRINT_FIRST_BYTES, this.stream.end));
       }
 
       hash = (0, _crypto.calculateMD5)(this.stream.bytes.subarray(0, FINGERPRINT_FIRST_BYTES), 0, FINGERPRINT_FIRST_BYTES);
     }
 
     let fingerprint = '';
 
-    for (const hashPart of hash) {
-      const hex = hashPart.toString(16);
+    for (let i = 0, ii = hash.length; i < ii; i++) {
+      const hex = hash[i].toString(16);
       fingerprint += hex.length === 1 ? '0' + hex : hex;
     }
 
     return (0, _util.shadow)(this, 'fingerprint', fingerprint);
   }
 
   _getLinearizationPage(pageIndex) {
     const {
@@ -31083,16 +31083,23 @@ var CFFParser = function CFFParserClosur
           return true;
         } else {
           validationCommand = CharstringValidationData[value];
         }
 
         if (validationCommand) {
           if (validationCommand.stem) {
             state.hints += stackSize >> 1;
+
+            if (value === 3 || value === 23) {
+              state.hasVStems = true;
+            } else if (state.hasVStems && (value === 1 || value === 18)) {
+              (0, _util.warn)('CFF stem hints are in wrong order');
+              data[j - 1] = value === 1 ? 3 : 23;
+            }
           }
 
           if ('min' in validationCommand) {
             if (!state.undefStack && stackSize < validationCommand.min) {
               (0, _util.warn)('Not enough parameters for ' + validationCommand.id + '; actual: ' + stackSize + ', expected: ' + validationCommand.min);
               return false;
             }
           }
@@ -31152,17 +31159,18 @@ var CFFParser = function CFFParserClosur
         var state = {
           callDepth: 0,
           stackSize: 0,
           stack: [],
           undefStack: true,
           hints: 0,
           firstStackClearing: true,
           seac: null,
-          width: null
+          width: null,
+          hasVStems: false
         };
         var valid = true;
         var localSubrToUse = null;
         var privateDictToUse = privateDict;
 
         if (fdSelect && fdArray.length) {
           var fdIndex = fdSelect.getFDIndex(i);
 
--- a/browser/extensions/pdfjs/moz.yaml
+++ b/browser/extensions/pdfjs/moz.yaml
@@ -15,15 +15,15 @@ origin:
   description: Portable Document Format (PDF) viewer that is built with HTML5
 
   # Full URL for the package's homepage/etc
   # Usually different from repository url
   url: https://github.com/mozilla/pdf.js
 
   # Human-readable identifier for this version/release
   # Generally "version NNN", "tag SSS", "bookmark SSS"
-  release: version 2.1.145
+  release: version 2.1.153
 
   # The package's license, where possible using the mnemonic from
   # https://spdx.org/licenses/
   # Multiple licenses can be specified (as a YAML list)
   # A "LICENSE" file must exist containing the full license text
   license: Apache-2.0
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 116
+Version 117
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-115...release-116
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-116...release-117
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.4.1
 - react-dom @16.4.1
 - webpack @3.12.0
--- a/devtools/client/debugger/new/src/actions/sources/newSources.js
+++ b/devtools/client/debugger/new/src/actions/sources/newSources.js
@@ -35,17 +35,17 @@ function createOriginalSource(
   originalUrl,
   generatedSource,
   sourceMaps
 ): Source {
   return {
     url: originalUrl,
     relativeUrl: originalUrl,
     id: generatedToOriginalId(generatedSource.id, originalUrl),
-    thread: "",
+    thread: generatedSource.thread,
     isPrettyPrinted: false,
     isWasm: false,
     isBlackBoxed: false,
     loadedState: "unloaded"
   };
 }
 
 function loadSourceMaps(sources: Source[]) {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9747,17 +9747,34 @@ nsresult nsDocShell::DoURILoad(nsDocShel
                "DoURILoad thinks this is a frame and InternalLoad does not");
 
     // Only allow URLs able to return data in iframes.
     bool doesNotReturnData = false;
     NS_URIChainHasFlags(aLoadState->URI(),
                         nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
                         &doesNotReturnData);
     if (doesNotReturnData) {
-      return NS_ERROR_UNKNOWN_PROTOCOL;
+      bool popupBlocked = true;
+
+      // Let's consider external protocols as popups and let's check if the page
+      // is allowed to open them without abuse regardless of allowed events
+      if (PopupBlocker::GetPopupControlState() <= PopupBlocker::openBlocked) {
+        popupBlocked = !PopupBlocker::TryUsePopupOpeningToken();
+      } else {
+        nsCOMPtr<nsINode> loadingNode =
+            mScriptGlobal->AsOuter()->GetFrameElementInternal();
+        if (loadingNode) {
+          popupBlocked = !PopupBlocker::CanShowPopupByPermission(
+              loadingNode->NodePrincipal());
+        }
+      }
+
+      if (popupBlocked) {
+        return NS_ERROR_UNKNOWN_PROTOCOL;
+      }
     }
 
     // Only allow view-source scheme in top-level docshells. view-source is
     // the only scheme to which this applies at the moment due to potential
     // timing attacks to read data from cross-origin iframes. If this widens
     // we should add a protocol flag for whether the scheme is allowed in
     // frames and use something like nsNetUtil::NS_URIChainHasFlags.
     nsCOMPtr<nsIURI> tempURI = aLoadState->URI();
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -766,16 +766,45 @@ constexpr auto kSkipSelfHosted = JS::Sav
     aRv.Throw(NS_ERROR_FAILURE);
     return false;
   }
 
   return ReportingHeader::HasReportingHeaderForOrigin(
       NS_ConvertUTF16toUTF8(aOrigin));
 }
 
+/* static */ PopupBlockerState ChromeUtils::GetPopupControlState(
+    GlobalObject& aGlobal) {
+  switch (PopupBlocker::GetPopupControlState()) {
+    case PopupBlocker::PopupControlState::openAllowed:
+      return PopupBlockerState::OpenAllowed;
+
+    case PopupBlocker::PopupControlState::openControlled:
+      return PopupBlockerState::OpenControlled;
+
+    case PopupBlocker::PopupControlState::openBlocked:
+      return PopupBlockerState::OpenBlocked;
+
+    case PopupBlocker::PopupControlState::openAbused:
+      return PopupBlockerState::OpenAbused;
+
+    case PopupBlocker::PopupControlState::openOverridden:
+      return PopupBlockerState::OpenOverridden;
+
+    default:
+      MOZ_CRASH(
+          "PopupBlocker::PopupControlState and PopupBlockerState are out of "
+          "sync");
+  }
+}
+
+/* static */ bool ChromeUtils::IsPopupTokenUnused(GlobalObject& aGlobal) {
+  return PopupBlocker::IsPopupOpeningTokenUnused();
+}
+
 /* static */ void ChromeUtils::RegisterWindowActor(
     const GlobalObject& aGlobal, const nsAString& aName,
     const WindowActorOptions& aOptions, ErrorResult& aRv) {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   RefPtr<JSWindowActorService> service = JSWindowActorService::GetSingleton();
   service->RegisterWindowActor(aName, aOptions, aRv);
 }
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -169,16 +169,20 @@ class ChromeUtils {
   static void GetRootBrowsingContexts(
       GlobalObject& aGlobal,
       nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts);
 
   static bool HasReportingHeaderForOrigin(GlobalObject& global,
                                           const nsAString& aOrigin,
                                           ErrorResult& aRv);
 
+  static PopupBlockerState GetPopupControlState(GlobalObject& aGlobal);
+
+  static bool IsPopupTokenUnused(GlobalObject& aGlobal);
+
   static void RegisterWindowActor(const GlobalObject& aGlobal,
                                   const nsAString& aName,
                                   const WindowActorOptions& aOptions,
                                   ErrorResult& aRv);
 };
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -277,17 +277,16 @@
 #include "mozilla/DocLoadingTimelineMarker.h"
 
 #include "mozilla/dom/WindowGlobalChild.h"
 
 #include "nsISpeculativeConnect.h"
 
 #include "mozilla/MediaManager.h"
 
-#include "nsIURIClassifier.h"
 #include "nsIURIMutator.h"
 #include "mozilla/DocumentStyleRootIterator.h"
 #include "mozilla/PendingFullscreenEvent.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "nsHTMLTags.h"
 #include "NodeUbiReporting.h"
 #include "nsICookieService.h"
@@ -331,75 +330,16 @@ static nsresult GetHttpChannelHelper(nsI
   }
 
   httpChannel = do_QueryInterface(baseChannel);
   httpChannel.forget(aHttpChannel);
 
   return NS_OK;
 }
 
-////////////////////////////////////////////////////////////////////
-// PrincipalFlashClassifier
-
-// Classify the flash based on the document principal.
-// The usage of this class is as follows:
-//
-// 1) Call AsyncClassify() as early as possible to asynchronously do
-//    classification against all the flash blocking related tables
-//    via nsIURIClassifier.asyncClassifyLocalWithTables.
-//
-// 2) At any time you need the classification result, call Result()
-//    and it is guaranteed to give you the result. Note that you have
-//    to specify "aIsThirdParty" to the function so please make sure
-//    you can already correctly decide if the document is third-party.
-//
-//    Behind the scenes, the sync classification API
-//    (nsIURIClassifier.classifyLocalWithTable) may be called as a fallback to
-//    synchronously get the result if the asyncClassifyLocalWithTables hasn't
-//    been done yet.
-//
-// 3) You can call Result() as many times as you want and only the first time
-//    it may unfortunately call the blocking sync API. The subsequent call
-//    will just return the result that came out at the first time.
-//
-class PrincipalFlashClassifier final : public nsIURIClassifierCallback {
- public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIURICLASSIFIERCALLBACK
-
-  PrincipalFlashClassifier();
-
-  // Fire async classification based on the given principal.
-  void AsyncClassify(nsIPrincipal* aPrincipal);
-
-  // Would block if the result hasn't come out.
-  mozilla::dom::FlashClassification ClassifyMaybeSync(nsIPrincipal* aPrincipal,
-                                                      bool aIsThirdParty);
-
- private:
-  ~PrincipalFlashClassifier() = default;
-
-  void Reset();
-  bool EnsureUriClassifier();
-  mozilla::dom::FlashClassification CheckIfClassifyNeeded(
-      nsIPrincipal* aPrincipal);
-  mozilla::dom::FlashClassification Resolve(bool aIsThirdParty);
-  mozilla::dom::FlashClassification AsyncClassifyInternal(
-      nsIPrincipal* aPrincipal);
-  void GetClassificationTables(bool aIsThirdParty, nsACString& aTables);
-
-  // For the fallback sync classification.
-  nsCOMPtr<nsIURI> mClassificationURI;
-
-  nsCOMPtr<nsIURIClassifier> mUriClassifier;
-  bool mAsyncClassified;
-  nsTArray<nsCString> mMatchedTables;
-  mozilla::dom::FlashClassification mResult;
-};
-
 }  // namespace dom
 }  // namespace mozilla
 
 extern bool sDisablePrefetchHTTPSPref;
 
 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
 
 nsIdentifierMapEntry::nsIdentifierMapEntry(
@@ -1390,18 +1330,17 @@ Document::Document(const char* aContentT
       mHasUserInteractionTimerScheduled(false),
       mUserGestureActivated(false),
       mStackRefCnt(0),
       mUpdateNestLevel(0),
       mViewportType(Unknown),
       mViewportOverflowType(ViewportOverflowType::NoOverflow),
       mSubDocuments(nullptr),
       mHeaderData(nullptr),
-      mPrincipalFlashClassifier(new PrincipalFlashClassifier()),
-      mFlashClassification(FlashClassification::Unclassified),
+      mFlashClassification(FlashClassification::Unknown),
       mBoxObjectTable(nullptr),
       mCurrentOrientationAngle(0),
       mCurrentOrientationType(OrientationType::Portrait_primary),
       mServoRestyleRootDirtyBits(0),
       mThrowOnDynamicMarkupInsertionCounter(0),
       mIgnoreOpensDuringUnloadCounter(0),
       mDocLWTheme(Doc_Theme_Uninitialized),
       mSavedResolution(1.0f) {
@@ -2623,21 +2562,16 @@ nsresult Document::StartDocumentLoad(con
   // the CSP defines frame-ancestors.
   if (!FramingChecker::CheckFrameOptions(aChannel, docShell, NodePrincipal())) {
     MOZ_LOG(gCspPRLog, LogLevel::Debug,
             ("XFO doesn't like frame's ancestry, not loading."));
     // stop!  ERROR page!
     aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
   }
 
-  // Perform a async flash classification based on the doc principal
-  // in an early stage to reduce the blocking time.
-  mFlashClassification = FlashClassification::Unclassified;
-  mPrincipalFlashClassifier->AsyncClassify(GetPrincipal());
-
   return NS_OK;
 }
 
 void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
   for (uint32_t i = 0; i < aMessages.Length(); ++i) {
     nsAutoString messageTag;
     aMessages[i]->GetTag(messageTag);
 
@@ -12031,527 +11965,184 @@ Document* Document::GetSameTypeParentDoc
     return nullptr;
   }
 
   return parent->GetDocument();
 }
 
 /**
  * Retrieves the classification of the Flash plugins in the document based on
- * the classification lists. We perform AsyncInitFlashClassification on
- * StartDocumentLoad() and the result may not be initialized when this function
- * gets called. In that case, We can only unfortunately have a blocking wait.
- *
- * For more information, see
+ * the classification lists. For more information, see
  * toolkit/components/url-classifier/flash-block-lists.rst
  */
-FlashClassification Document::PrincipalFlashClassification() {
-  MOZ_ASSERT(mPrincipalFlashClassifier);
-  return mPrincipalFlashClassifier->ClassifyMaybeSync(NodePrincipal(),
-                                                      IsThirdParty());
-}
-
-/**
- * Helper function for |Document::PrincipalFlashClassification|
- *
- * Adds a table name string to a table list (a comma separated string). The
- * table will not be added if the name is an empty string.
- */
-static void MaybeAddTableToTableList(const nsACString& aTableNames,
-                                     nsACString& aTableList) {
-  if (aTableNames.IsEmpty()) {
-    return;
-  }
-  if (!aTableList.IsEmpty()) {
-    aTableList.AppendLiteral(",");
-  }
-  aTableList.Append(aTableNames);
-}
-
-/**
- * Helper function for |Document::PrincipalFlashClassification|
- *
- * Takes an array of table names and a comma separated list of table names
- * Returns |true| if any table name in the array matches a table name in the
- * comma separated list.
- */
-static bool ArrayContainsTable(const nsTArray<nsCString>& aTableArray,
-                               const nsACString& aTableNames) {
-  for (const nsCString& table : aTableArray) {
-    // This check is sufficient because table names cannot contain commas and
-    // cannot contain another existing table name.
-    if (FindInReadable(table, aTableNames)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-namespace {
-
-static const char* gCallbackPrefs[] = {
-    // We only need to register string-typed preferences.
-    "urlclassifier.flashAllowTable",
-    "urlclassifier.flashAllowExceptTable",
-    "urlclassifier.flashTable",
-    "urlclassifier.flashExceptTable",
-    "urlclassifier.flashSubDocTable",
-    "urlclassifier.flashSubDocExceptTable",
-    nullptr,
-};
-
-// An object to store all preferences we need for flash blocking feature.
-struct PrefStore {
-  PrefStore() : mFlashBlockEnabled(false), mPluginsHttpOnly(false) {
-    Preferences::AddBoolVarCache(&mFlashBlockEnabled,
-                                 "plugins.flashBlock.enabled");
-    Preferences::AddBoolVarCache(&mPluginsHttpOnly, "plugins.http_https_only");
-
-    Preferences::RegisterCallbacks(
-        PREF_CHANGE_METHOD(PrefStore::UpdateStringPrefs), gCallbackPrefs, this);
-
-    UpdateStringPrefs();
-  }
-
-  ~PrefStore() {
-    Preferences::UnregisterCallbacks(
-        PREF_CHANGE_METHOD(PrefStore::UpdateStringPrefs), gCallbackPrefs, this);
-  }
-
-  void UpdateStringPrefs(const char* aPref = nullptr) {
-    Preferences::GetCString("urlclassifier.flashAllowTable", mAllowTables);
-    Preferences::GetCString("urlclassifier.flashAllowExceptTable",
-                            mAllowExceptionsTables);
-    Preferences::GetCString("urlclassifier.flashTable", mDenyTables);
-    Preferences::GetCString("urlclassifier.flashExceptTable",
-                            mDenyExceptionsTables);
-    Preferences::GetCString("urlclassifier.flashSubDocTable",
-                            mSubDocDenyTables);
-    Preferences::GetCString("urlclassifier.flashSubDocExceptTable",
-                            mSubDocDenyExceptionsTables);
-  }
-
-  bool mFlashBlockEnabled;
-  bool mPluginsHttpOnly;
-
-  nsCString mAllowTables;
-  nsCString mAllowExceptionsTables;
-  nsCString mDenyTables;
-  nsCString mDenyExceptionsTables;
-  nsCString mSubDocDenyTables;
-  nsCString mSubDocDenyExceptionsTables;
-};
-
-static const PrefStore& GetPrefStore() {
-  static UniquePtr<PrefStore> sPrefStore;
-  if (!sPrefStore) {
-    sPrefStore.reset(new PrefStore());
-    ClearOnShutdown(&sPrefStore);
-  }
-  return *sPrefStore;
-}
-
-}  // end of unnamed namespace.
-
-////////////////////////////////////////////////////////////////////
-// PrincipalFlashClassifier implementation.
-
-NS_IMPL_ISUPPORTS(PrincipalFlashClassifier, nsIURIClassifierCallback)
-
-PrincipalFlashClassifier::PrincipalFlashClassifier() { Reset(); }
-
-void PrincipalFlashClassifier::Reset() {
-  mAsyncClassified = false;
-  mMatchedTables.Clear();
-  mResult = FlashClassification::Unclassified;
-}
-
-void PrincipalFlashClassifier::GetClassificationTables(bool aIsThirdParty,
-                                                       nsACString& aTables) {
-  aTables.Truncate();
-  auto& prefs = GetPrefStore();
-
-  MaybeAddTableToTableList(prefs.mAllowTables, aTables);
-  MaybeAddTableToTableList(prefs.mAllowExceptionsTables, aTables);
-  MaybeAddTableToTableList(prefs.mDenyTables, aTables);
-  MaybeAddTableToTableList(prefs.mDenyExceptionsTables, aTables);
-
-  if (aIsThirdParty) {
-    MaybeAddTableToTableList(prefs.mSubDocDenyTables, aTables);
-    MaybeAddTableToTableList(prefs.mSubDocDenyExceptionsTables, aTables);
-  }
-}
-
-bool PrincipalFlashClassifier::EnsureUriClassifier() {
-  if (!mUriClassifier) {
-    mUriClassifier = do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID);
-  }
-
-  return !!mUriClassifier;
-}
-
-FlashClassification PrincipalFlashClassifier::ClassifyMaybeSync(
-    nsIPrincipal* aPrincipal, bool aIsThirdParty) {
-  if (FlashClassification::Unclassified != mResult) {
-    // We already have the result. Just return it.
-    return mResult;
-  }
-
-  // TODO: Bug 1342333 - Entirely remove the use of the sync API
-  // (ClassifyLocalWithTables).
-  if (!mAsyncClassified) {
-    //
-    // We may
-    //   1) have called AsyncClassifyLocalWithTables but OnClassifyComplete
-    //      hasn't been called.
-    //   2) haven't even called AsyncClassifyLocalWithTables.
-    //
-    // In both cases we need to do the synchronous classification as the
-    // fallback.
-    //
-
-    if (!EnsureUriClassifier()) {
-      return FlashClassification::Denied;
-    }
-    mResult = CheckIfClassifyNeeded(aPrincipal);
-    if (FlashClassification::Unclassified != mResult) {
-      return mResult;
-    }
-
-    nsresult rv;
-    nsAutoCString classificationTables;
-    GetClassificationTables(aIsThirdParty, classificationTables);
-
-    if (!mClassificationURI) {
-      rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI));
-      if (NS_FAILED(rv) || !mClassificationURI) {
-        mResult = FlashClassification::Denied;
-        return mResult;
-      }
-    }
-
-    rv = mUriClassifier->ClassifyLocalWithTables(
-        mClassificationURI, classificationTables, mMatchedTables);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      if (rv == NS_ERROR_MALFORMED_URI) {
-        // This means that the URI had no hostname (ex: file://doc.html). In
-        // this case, we allow the default (Unknown plugin) behavior.
-        mResult = FlashClassification::Unknown;
-      } else {
-        mResult = FlashClassification::Denied;
-      }
-      return mResult;
-    }
-  }
-
-  // Resolve the result based on mMatchedTables and aIsThirdParty.
-  mResult = Resolve(aIsThirdParty);
-  MOZ_ASSERT(FlashClassification::Unclassified != mResult);
-
-  // The subsequent call of Result() will return the resolved result
-  // and never reach here until Reset() is called.
-  return mResult;
-}
-
-/*virtual*/ nsresult PrincipalFlashClassifier::OnClassifyComplete(
-    nsresult /*aErrorCode*/,
-    const nsACString& aLists,  // Only this matters.
-    const nsACString& /*aProvider*/, const nsACString& /*aPrefix*/) {
-  mAsyncClassified = true;
-
-  if (FlashClassification::Unclassified != mResult) {
-    // Result() has been called prior to this callback.
-    return NS_OK;
-  }
-
-  // TODO: Bug 1364804 - We should use a callback type which notifies
-  // the result as a string array rather than a formatted string.
-
-  // We only populate the matched list without resolving the classification
-  // result because we are not sure if the parent doc has been properly set.
-  // We also parse the comma-separated tables to array. (the code is copied
-  // from Classifier::SplitTables.)
-  nsACString::const_iterator begin, iter, end;
-  aLists.BeginReading(begin);
-  aLists.EndReading(end);
-  while (begin != end) {
-    iter = begin;
-    FindCharInReadable(',', iter, end);
-    nsDependentCSubstring table = Substring(begin, iter);
-    if (!table.IsEmpty()) {
-      mMatchedTables.AppendElement(Substring(begin, iter));
-    }
-    begin = iter;
-    if (begin != end) {
-      begin++;
-    }
-  }
-
-  return NS_OK;
-}
-
-// We resolve the classification result based on aIsThirdParty
-// and the matched tables we got ealier on (via either sync or async API).
-FlashClassification PrincipalFlashClassifier::Resolve(bool aIsThirdParty) {
-  MOZ_ASSERT(FlashClassification::Unclassified == mResult,
-             "We already have resolved classification result.");
-
-  if (mMatchedTables.IsEmpty()) {
+FlashClassification Document::DocumentFlashClassification() {
+  // If neither pref is on, skip the null-principal and principal URI checks.
+  if (!StaticPrefs::plugins_http_https_only() &&
+      !StaticPrefs::plugins_flashBlock_enabled()) {
     return FlashClassification::Unknown;
   }
 
-  auto& prefs = GetPrefStore();
-  if (ArrayContainsTable(mMatchedTables, prefs.mDenyTables) &&
-      !ArrayContainsTable(mMatchedTables, prefs.mDenyExceptionsTables)) {
-    return FlashClassification::Denied;
-  } else if (ArrayContainsTable(mMatchedTables, prefs.mAllowTables) &&
-             !ArrayContainsTable(mMatchedTables,
-                                 prefs.mAllowExceptionsTables)) {
-    return FlashClassification::Allowed;
-  }
-
-  if (aIsThirdParty &&
-      ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyTables) &&
-      !ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyExceptionsTables)) {
-    return FlashClassification::Denied;
-  }
-
-  return FlashClassification::Unknown;
-}
-
-void PrincipalFlashClassifier::AsyncClassify(nsIPrincipal* aPrincipal) {
-  MOZ_ASSERT(FlashClassification::Unclassified == mResult,
-             "The old classification result should be reset first.");
-  Reset();
-  mResult = AsyncClassifyInternal(aPrincipal);
-}
-
-FlashClassification PrincipalFlashClassifier::CheckIfClassifyNeeded(
-    nsIPrincipal* aPrincipal) {
-  nsresult rv;
-
-  auto& prefs = GetPrefStore();
-
-  // If neither pref is on, skip the null-principal and principal URI checks.
-  if (prefs.mPluginsHttpOnly && !prefs.mFlashBlockEnabled) {
-    return FlashClassification::Unknown;
-  }
-
-  nsCOMPtr<nsIPrincipal> principal = aPrincipal;
-  if (principal->GetIsNullPrincipal()) {
+  if (NodePrincipal()->GetIsNullPrincipal()) {
     return FlashClassification::Denied;
   }
 
   nsCOMPtr<nsIURI> classificationURI;
-  rv = principal->GetURI(getter_AddRefs(classificationURI));
+  nsresult rv = NodePrincipal()->GetURI(getter_AddRefs(classificationURI));
   if (NS_FAILED(rv) || !classificationURI) {
     return FlashClassification::Denied;
   }
 
-  if (prefs.mPluginsHttpOnly) {
+  if (StaticPrefs::plugins_http_https_only()) {
     // Only allow plugins for documents from an HTTP/HTTPS origin. This should
     // allow dependent data: URIs to load plugins, but not:
     // * chrome documents
     // * "bare" data: loads
     // * FTP/gopher/file
     nsAutoCString scheme;
     rv = classificationURI->GetScheme(scheme);
     if (NS_WARN_IF(NS_FAILED(rv)) ||
         !(scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https"))) {
       return FlashClassification::Denied;
     }
   }
 
   // If flash blocking is disabled, it is equivalent to all sites being
   // on neither list.
-  if (!prefs.mFlashBlockEnabled) {
-    return FlashClassification::Unknown;
-  }
-
-  return FlashClassification::Unclassified;
-}
-
-// Using nsIURIClassifier.asyncClassifyLocalWithTables to do classification
-// against the flash related tables based on the given principal.
-FlashClassification PrincipalFlashClassifier::AsyncClassifyInternal(
-    nsIPrincipal* aPrincipal) {
-  auto result = CheckIfClassifyNeeded(aPrincipal);
-  if (FlashClassification::Unclassified != result) {
-    return result;
-  }
-
-  // We haven't been able to decide if it's a third party document
-  // since determining if a document is third-party may depend on its
-  // parent document. At the time we call AsyncClassifyInternal
-  // (i.e. StartDocumentLoad) the parent document may not have been
-  // set. As a result, we wait until Resolve() to be called to
-  // take "is third party" into account. At this point, we just assume
-  // it's third-party to include every list.
-  nsAutoCString tables;
-  GetClassificationTables(true, tables);
-
-  if (tables.IsEmpty()) {
+  if (!StaticPrefs::plugins_flashBlock_enabled()) {
     return FlashClassification::Unknown;
   }
 
-  if (!EnsureUriClassifier()) {
-    return FlashClassification::Denied;
-  }
-
-  nsresult rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI));
-  if (NS_FAILED(rv) || !mClassificationURI) {
-    return FlashClassification::Denied;
-  }
-
-  // We don't support extra entries by pref for this classifier.
-  rv = mUriClassifier->AsyncClassifyLocalWithTables(
-      mClassificationURI, tables, nsTArray<nsCString>(), nsTArray<nsCString>(),
-      this);
-
-  if (NS_FAILED(rv)) {
-    if (rv == NS_ERROR_MALFORMED_URI) {
-      // This means that the URI had no hostname (ex: file://doc.html). In this
-      // case, we allow the default (Unknown plugin) behavior.
-      return FlashClassification::Unknown;
-    } else {
-      return FlashClassification::Denied;
-    }
-  }
-
-  return FlashClassification::Unclassified;
-}
-
-FlashClassification Document::ComputeFlashClassification() {
-  nsCOMPtr<nsIDocShellTreeItem> current = this->GetDocShell();
-  if (!current) {
-    return FlashClassification::Denied;
-  }
-  nsCOMPtr<nsIDocShellTreeItem> parent;
-  DebugOnly<nsresult> rv = current->GetSameTypeParent(getter_AddRefs(parent));
-  MOZ_ASSERT(NS_SUCCEEDED(rv),
-             "nsIDocShellTreeItem::GetSameTypeParent should never fail");
-
-  bool isTopLevel = !parent;
-  FlashClassification classification;
-  if (isTopLevel) {
-    classification = PrincipalFlashClassification();
-  } else {
-    nsCOMPtr<Document> parentDocument = GetParentDocument();
-    if (!parentDocument) {
-      return FlashClassification::Denied;
-    }
-    FlashClassification parentClassification =
-        parentDocument->DocumentFlashClassification();
-
-    if (parentClassification == FlashClassification::Denied) {
-      classification = FlashClassification::Denied;
-    } else {
-      classification = PrincipalFlashClassification();
-
-      // Allow unknown children to inherit allowed status from parent, but
-      // do not allow denied children to do so.
-      if (classification == FlashClassification::Unknown &&
-          parentClassification == FlashClassification::Allowed) {
-        classification = FlashClassification::Allowed;
-      }
-    }
-  }
-
-  return classification;
-}
-
-/**
- * Retrieves the classification of plugins in this document. This is dependent
- * on the classification of this document and all parent documents.
- * This function is infallible - It must return some classification that
- * callers can act on.
- *
- * This function will NOT return FlashClassification::Unclassified
- */
-FlashClassification Document::DocumentFlashClassification() {
-  if (mFlashClassification == FlashClassification::Unclassified) {
-    FlashClassification result = ComputeFlashClassification();
-    mFlashClassification = result;
-    MOZ_ASSERT(
-        result != FlashClassification::Unclassified,
-        "Document::GetPluginClassification should never return Unclassified");
+  if (mFlashClassification == FlashClassification::Unknown) {
+    mFlashClassification = DocumentFlashClassificationInternal();
   }
 
   return mFlashClassification;
 }
 
 /**
- * Initializes |mIsThirdParty| if necessary and returns its value. The value
- * returned represents whether this document should be considered Third-Party.
+ * Initializes |mIsThirdPartyForFlashClassifier| if necessary and returns its
+ * value. The value returned represents whether this document should be
+ * considered Third-Party.
  *
  * A top-level document cannot be a considered Third-Party; only subdocuments
  * may. For a subdocument to be considered Third-Party, it must meet ANY ONE
  * of the following requirements:
  *  - The document's parent is Third-Party
  *  - The document has a different scheme (http/https) than its parent document
  *  - The document's domain and subdomain do not match those of its parent
  *    document.
  *
  * If there is an error in determining whether the document is Third-Party,
  * it will be assumed to be Third-Party for security reasons.
  */
-bool Document::IsThirdParty() {
-  if (mIsThirdParty.isSome()) {
-    return mIsThirdParty.value();
+bool Document::IsThirdPartyForFlashClassifier() {
+  if (mIsThirdPartyForFlashClassifier.isSome()) {
+    return mIsThirdPartyForFlashClassifier.value();
   }
 
   nsCOMPtr<nsIDocShellTreeItem> docshell = this->GetDocShell();
   if (!docshell) {
-    mIsThirdParty.emplace(true);
-    return mIsThirdParty.value();
+    mIsThirdPartyForFlashClassifier.emplace(true);
+    return mIsThirdPartyForFlashClassifier.value();
   }
 
   nsCOMPtr<nsIDocShellTreeItem> parent;
   nsresult rv = docshell->GetSameTypeParent(getter_AddRefs(parent));
   MOZ_ASSERT(NS_SUCCEEDED(rv),
              "nsIDocShellTreeItem::GetSameTypeParent should never fail");
   bool isTopLevel = !parent;
 
   if (isTopLevel) {
-    mIsThirdParty.emplace(false);
-    return mIsThirdParty.value();
+    mIsThirdPartyForFlashClassifier.emplace(false);
+    return mIsThirdPartyForFlashClassifier.value();
   }
 
   nsCOMPtr<Document> parentDocument = GetParentDocument();
   if (!parentDocument) {
     // Failure
-    mIsThirdParty.emplace(true);
-    return mIsThirdParty.value();
-  }
-
-  if (parentDocument->IsThirdParty()) {
-    mIsThirdParty.emplace(true);
-    return mIsThirdParty.value();
+    mIsThirdPartyForFlashClassifier.emplace(true);
+    return mIsThirdPartyForFlashClassifier.value();
+  }
+
+  if (parentDocument->IsThirdPartyForFlashClassifier()) {
+    mIsThirdPartyForFlashClassifier.emplace(true);
+    return mIsThirdPartyForFlashClassifier.value();
   }
 
   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
   nsCOMPtr<nsIPrincipal> parentPrincipal = parentDocument->GetPrincipal();
 
   bool principalsMatch = false;
   rv = principal->Equals(parentPrincipal, &principalsMatch);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     // Failure
-    mIsThirdParty.emplace(true);
-    return mIsThirdParty.value();
+    mIsThirdPartyForFlashClassifier.emplace(true);
+    return mIsThirdPartyForFlashClassifier.value();
   }
 
   if (!principalsMatch) {
-    mIsThirdParty.emplace(true);
-    return mIsThirdParty.value();
+    mIsThirdPartyForFlashClassifier.emplace(true);
+    return mIsThirdPartyForFlashClassifier.value();
   }
 
   // Fall-through. Document is not a Third-Party Document.
-  mIsThirdParty.emplace(false);
-  return mIsThirdParty.value();
+  mIsThirdPartyForFlashClassifier.emplace(false);
+  return mIsThirdPartyForFlashClassifier.value();
+}
+
+FlashClassification Document::DocumentFlashClassificationInternal() {
+  FlashClassification classification = FlashClassification::Unknown;
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(GetChannel());
+  if (httpChannel) {
+    nsIHttpChannel::FlashPluginState state = nsIHttpChannel::FlashPluginUnknown;
+    httpChannel->GetFlashPluginState(&state);
+
+    // Allow unknown children to inherit allowed status from parent, but do not
+    // allow denied children to do so.
+
+    if (state == nsIHttpChannel::FlashPluginDeniedInSubdocuments &&
+        IsThirdPartyForFlashClassifier()) {
+      return FlashClassification::Denied;
+    }
+
+    if (state == nsIHttpChannel::FlashPluginDenied) {
+      return FlashClassification::Denied;
+    }
+
+    if (state == nsIHttpChannel::FlashPluginAllowed) {
+      classification = FlashClassification::Allowed;
+    }
+  }
+
+  if (IsTopLevelContentDocument()) {
+    return classification;
+  }
+
+  Document* parentDocument = GetParentDocument();
+  if (!parentDocument) {
+    return FlashClassification::Denied;
+  }
+
+  FlashClassification parentClassification =
+      parentDocument->DocumentFlashClassification();
+
+  if (parentClassification == FlashClassification::Denied) {
+    return FlashClassification::Denied;
+  }
+
+  // Allow unknown children to inherit allowed status from parent, but
+  // do not allow denied children to do so.
+  if (classification == FlashClassification::Unknown &&
+      parentClassification == FlashClassification::Allowed) {
+    return FlashClassification::Allowed;
+  }
+
+  return classification;
 }
 
 void Document::ClearStaleServoData() {
   DocumentStyleRootIterator iter(this);
   while (Element* root = iter.GetNextStyleRoot()) {
     RestyleManager::ClearServoDataFromSubtree(root);
   }
 }
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -422,19 +422,16 @@ class ExternalResourceMap {
 
   nsClassHashtable<nsURIHashKey, ExternalResource> mMap;
   nsRefPtrHashtable<nsURIHashKey, PendingLoad> mPendingLoads;
   bool mHaveShutDown;
 };
 
 //----------------------------------------------------------------------
 
-// For classifying a flash document based on its principal.
-class PrincipalFlashClassifier;
-
 // Document interface.  This is implemented by all document objects in
 // Gecko.
 class Document : public nsINode,
                  public DocumentOrShadowRoot,
                  public nsSupportsWeakReference,
                  public nsIRadioGroupContainer,
                  public nsIScriptObjectPrincipal,
                  public nsIApplicationCacheContainer,
@@ -3407,24 +3404,26 @@ class Document : public nsINode,
 
  private:
   void InitializeLocalization(nsTArray<nsString>& aResourceIds);
 
   void ParseWidthAndHeightInMetaViewport(const nsAString& aWidthString,
                                          const nsAString& aHeightString,
                                          const nsAString& aScaleString);
 
+  mozilla::dom::FlashClassification DocumentFlashClassificationInternal();
+
   nsTArray<nsString> mL10nResources;
 
   // The application cache that this document is associated with, if
   // any.  This can change during the lifetime of the document.
   nsCOMPtr<nsIApplicationCache> mApplicationCache;
 
  public:
-  bool IsThirdParty();
+  bool IsThirdPartyForFlashClassifier();
 
   bool IsScopedStyleEnabled();
 
   nsINode* GetServoRestyleRoot() const { return mServoRestyleRoot; }
 
   uint32_t GetServoRestyleRootDirtyBits() const {
     MOZ_ASSERT(mServoRestyleRoot);
     MOZ_ASSERT(mServoRestyleRootDirtyBits);
@@ -3515,24 +3514,16 @@ class Document : public nsINode,
    * Returns the title element of the document as defined by the HTML
    * specification, or null if there isn't one.  For documents whose root
    * element is an <svg:svg>, this is the first <svg:title> element that's a
    * child of the root.  For other documents, it's the first HTML title element
    * in the document.
    */
   Element* GetTitleElement();
 
-  // Retrieves the classification of the Flash plugins in the document based on
-  // the classification lists.
-  mozilla::dom::FlashClassification PrincipalFlashClassification();
-
-  // Attempts to determine the Flash classification of this page based on the
-  // the classification lists and the classification of parent documents.
-  mozilla::dom::FlashClassification ComputeFlashClassification();
-
   void RecordNavigationTiming(ReadyState aReadyState);
 
   // This method may fire a DOM event; if it does so it will happen
   // synchronously.
   void UpdateVisibilityState();
 
   // Recomputes the visibility state but doesn't set the new value.
   mozilla::dom::VisibilityState ComputeVisibilityState() const;
@@ -4289,21 +4280,21 @@ class Document : public nsINode,
   ViewportOverflowType mViewportOverflowType;
 
   PLDHashTable* mSubDocuments;
 
   DocHeaderData* mHeaderData;
 
   // For determining if this is a flash document which should be
   // blocked based on its principal.
-  RefPtr<PrincipalFlashClassifier> mPrincipalFlashClassifier;
   mozilla::dom::FlashClassification mFlashClassification;
-  // Do not use this value directly. Call the |IsThirdParty()| method, which
-  // caches its result here.
-  mozilla::Maybe<bool> mIsThirdParty;
+
+  // Do not use this value directly. Call the |IsThirdPartyForFlashClassifier()|
+  // method, which caches its result here.
+  mozilla::Maybe<bool> mIsThirdPartyForFlashClassifier;
 
   nsRevocableEventPtr<nsRunnableMethod<Document, void, false>>
       mPendingTitleChangeEvent;
 
   RefPtr<nsDOMNavigationTiming> mTiming;
 
   // Recorded time of change to 'loading' state.
   mozilla::TimeStamp mLoadingTimeStamp;
--- a/dom/base/PopupBlocker.cpp
+++ b/dom/base/PopupBlocker.cpp
@@ -136,16 +136,20 @@ PopupBlocker::GetPopupControlState() {
   if (!sUnusedPopupToken) {
     sUnusedPopupToken = true;
     return true;
   }
 
   return false;
 }
 
+/* static */ bool PopupBlocker::IsPopupOpeningTokenUnused() {
+  return sUnusedPopupToken;
+}
+
 /* static */ void PopupBlocker::PopupStatePusherCreated() {
   ++sPopupStatePusherCount;
 }
 
 /* static */ void PopupBlocker::PopupStatePusherDestroyed() {
   MOZ_ASSERT(sPopupStatePusherCount);
 
   if (!--sPopupStatePusherCount) {
--- a/dom/base/PopupBlocker.h
+++ b/dom/base/PopupBlocker.h
@@ -15,16 +15,17 @@ namespace mozilla {
 namespace dom {
 
 class PopupBlocker final {
  public:
   // Popup control state enum. The values in this enum must go from most
   // permissive to least permissive so that it's safe to push state in
   // all situations. Pushing popup state onto the stack never makes the
   // current popup state less permissive.
+  // Keep this in sync with PopupBlockerState webidl dictionary!
   enum PopupControlState {
     openAllowed = 0,  // open that window without worries
     openControlled,   // it's a popup, but allow it
     openBlocked,      // it's a popup, but not from an allowed event
     openAbused,       // it's a popup. disallow it, but allow domain override.
     openOverridden    // disallow window open
   };
 
@@ -42,16 +43,18 @@ class PopupBlocker final {
   // permissions. In this case, the caller should not block popups.
   static bool CanShowPopupByPermission(nsIPrincipal* aPrincipal);
 
   // This method returns true if the caller is allowed to show a popup, and it
   // consumes the popup token for the current event. There is just 1 popup
   // allowed per event.
   static bool TryUsePopupOpeningToken();
 
+  static bool IsPopupOpeningTokenUnused();
+
   static PopupBlocker::PopupControlState GetEventPopupControlState(
       WidgetEvent* aEvent, Event* aDOMEvent = nullptr);
 
   static void Initialize();
   static void Shutdown();
 };
 
 }  // namespace dom
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -375,16 +375,22 @@ partial namespace ChromeUtils {
    * Returns all the root BrowsingContexts.
    */
   [ChromeOnly]
   sequence<BrowsingContext> getRootBrowsingContexts();
 
   [ChromeOnly, Throws]
   boolean hasReportingHeaderForOrigin(DOMString aOrigin);
 
+  [ChromeOnly]
+  PopupBlockerState getPopupControlState();
+
+  [ChromeOnly]
+  boolean isPopupTokenUnused();
+
   [ChromeOnly, Throws]
   void registerWindowActor(DOMString aName, WindowActorOptions aOptions);
 };
 
 /**
  * Dictionaries duplicating IPDL types in dom/ipc/DOMTypes.ipdlh
  * Used by requestPerformanceMetrics
  */
@@ -546,8 +552,17 @@ enum Base64URLDecodePadding {
    */
   "reject"
 };
 
 dictionary Base64URLDecodeOptions {
   /** Specifies the padding mode for decoding the input. */
   required Base64URLDecodePadding padding;
 };
+
+// Keep this in sync with PopupBlocker::PopupControlState!
+enum PopupBlockerState {
+  "openAllowed",
+  "openControlled",
+  "openBlocked",
+  "openAbused",
+  "openOverridden",
+};
--- a/dom/html/test/chrome.ini
+++ b/dom/html/test/chrome.ini
@@ -2,8 +2,9 @@
 support-files =
   file_anchor_ping.html
   image.png
 
 [test_anchor_ping.html]
 skip-if = os == 'android'
 [test_bug1414077.html]
 [test_multipleFilePicker.html]
+[test_external_protocol_iframe.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_external_protocol_iframe.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for external protocol URLs blocked for iframes</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <div id='foo'><a href='#'>Click here to test this issue</a></div>
+  <script>
+
+SimpleTest.waitForExplicitFinish();
+
+let foo = document.getElementById('foo');
+foo.addEventListener('click', _ => {
+  is(ChromeUtils.getPopupControlState(), "openAllowed", "Click events allow popups");
+  ok(!ChromeUtils.isPopupTokenUnused(), "Popup token has not been used yet");
+
+  for (let i = 0; i < 10; ++i) {
+    let ifr = document.createElement('iframe');
+    ifr.src = "foo+bar:all_good";
+    document.body.appendChild(ifr);
+
+    is(ChromeUtils.getPopupControlState(), "openAllowed", "Click events allow popups");
+    ok(ChromeUtils.isPopupTokenUnused(), "Popup token has been used!");
+  }
+
+  SimpleTest.finish();
+
+}, {once: true});
+
+setTimeout(_ => {
+  sendMouseEvent({type:'click'}, 'foo');
+}, 0);
+
+  </script>
+</body>
+</html>
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5449,33 +5449,16 @@ bool ContentParent::DeallocPLoginReputat
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aActor);
 
   RefPtr<LoginReputationParent> actor =
       dont_AddRef(static_cast<LoginReputationParent*>(aActor));
   return true;
 }
 
-mozilla::ipc::IPCResult ContentParent::RecvClassifyLocal(
-    const URIParams& aURI, const nsCString& aTables, nsresult* aRv,
-    nsTArray<nsCString>* aResults) {
-  MOZ_ASSERT(aResults);
-  nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
-  if (!uri) {
-    return IPC_FAIL_NO_REASON(this);
-  }
-  nsCOMPtr<nsIURIClassifier> uriClassifier =
-      do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID);
-  if (!uriClassifier) {
-    return IPC_FAIL_NO_REASON(this);
-  }
-  *aRv = uriClassifier->ClassifyLocalWithTables(uri, aTables, *aResults);
-  return IPC_OK();
-}
-
 mozilla::ipc::IPCResult ContentParent::RecvFileCreationRequest(
     const nsID& aID, const nsString& aFullPath, const nsString& aType,
     const nsString& aName, const bool& aLastModifiedPassed,
     const int64_t& aLastModified, const bool& aExistenceCheck,
     const bool& aIsFromNsIFile) {
   // We allow the creation of File via this IPC call only for the 'file' process
   // or for testing.
   if (!mRemoteType.EqualsLiteral(FILE_REMOTE_TYPE) &&
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -588,20 +588,16 @@ class ContentParent final : public PCont
   }
 
   virtual bool DeallocPURLClassifierLocalParent(
       PURLClassifierLocalParent* aActor) override;
 
   virtual bool DeallocPURLClassifierParent(
       PURLClassifierParent* aActor) override;
 
-  virtual mozilla::ipc::IPCResult RecvClassifyLocal(
-      const URIParams& aURI, const nsCString& aTables, nsresult* aRv,
-      nsTArray<nsCString>* aResults) override;
-
   // Use the PHangMonitor channel to ask the child to repaint a tab.
   void PaintTabWhileInterruptingJS(TabParent* aTabParent, bool aForceRepaint,
                                    const layers::LayersObserverEpoch& aEpoch);
 
   // This function is called when we are about to load a document from an
   // HTTP(S), FTP or wyciwyg channel for a content process.  It is a useful
   // place to start to kick off work as early as possible in response to such
   // document loads.
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -836,20 +836,16 @@ parent:
 
     async PPresentation();
 
     async CreateAudioIPCConnection() returns (FileDescriptor fd);
 
     sync PURLClassifier(Principal principal, bool useTrackingProtection)
         returns (bool success);
 
-    sync ClassifyLocal(URIParams uri, nsCString tables)
-        returns (nsresult rv, nsCString[] results);
-
-    // The async version of ClassifyLocal.
     async PURLClassifierLocal(URIParams uri, IPCURLClassifierFeature[] features);
 
     async PLoginReputation(URIParams formURI);
 
     // Services remoting
 
     async StartVisitedQuery(URIParams uri);
     async SetURITitle(URIParams uri, nsString title);
--- a/dom/ipc/PURLClassifierLocal.ipdl
+++ b/dom/ipc/PURLClassifierLocal.ipdl
@@ -1,23 +1,27 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* 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 protocol PContent;
+
 include PURLClassifierInfo;
 
+using refcounted class nsIURI from "mozilla/ipc/URIUtils.h";
+
 namespace mozilla {
 namespace dom {
 
 struct URLClassifierLocalResult
 {
+  nsIURI uri;
   nsCString featureName;
   nsCString matchingList;
 };
 
 protocol PURLClassifierLocal
 {
   manager PContent;
 
--- a/dom/ipc/URLClassifierChild.h
+++ b/dom/ipc/URLClassifierChild.h
@@ -4,16 +4,17 @@
  * 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_dom_URLClassifierChild_h
 #define mozilla_dom_URLClassifierChild_h
 
 #include "mozilla/dom/PURLClassifierChild.h"
 #include "mozilla/dom/PURLClassifierLocalChild.h"
+#include "mozilla/ipc/URIUtils.h"
 #include "mozilla/net/UrlClassifierFeatureResult.h"
 #include "nsIURIClassifier.h"
 #include "nsIUrlClassifierFeature.h"
 
 namespace mozilla {
 namespace dom {
 
 class URLClassifierChild : public PURLClassifierChild {
@@ -58,18 +59,24 @@ class URLClassifierLocalChild : public P
         if (NS_WARN_IF(NS_FAILED(rv))) {
           continue;
         }
 
         if (result.featureName() != name) {
           continue;
         }
 
+        RefPtr<nsIURI> uri = result.uri();
+        if (NS_WARN_IF(!uri)) {
+          continue;
+        }
+
         RefPtr<net::UrlClassifierFeatureResult> r =
-            new net::UrlClassifierFeatureResult(feature, result.matchingList());
+            new net::UrlClassifierFeatureResult(uri, feature,
+                                                result.matchingList());
         finalResults.AppendElement(r);
         break;
       }
     }
 
     mCallback->OnClassifyComplete(finalResults);
     return IPC_OK();
   }
--- a/dom/ipc/URLClassifierParent.cpp
+++ b/dom/ipc/URLClassifierParent.cpp
@@ -48,18 +48,18 @@ mozilla::ipc::IPCResult URLClassifierPar
 namespace {
 
 // This class implements a nsIUrlClassifierFeature on the parent side, starting
 // from an IPC data struct.
 class IPCFeature final : public nsIUrlClassifierFeature {
  public:
   NS_DECL_ISUPPORTS
 
-  explicit IPCFeature(const IPCURLClassifierFeature& aFeature)
-      : mIPCFeature(aFeature) {}
+  IPCFeature(nsIURI* aURI, const IPCURLClassifierFeature& aFeature)
+      : mURI(aURI), mIPCFeature(aFeature) {}
 
   NS_IMETHOD
   GetName(nsACString& aName) override {
     aName = mIPCFeature.featureName();
     return NS_OK;
   }
 
   NS_IMETHOD
@@ -97,19 +97,32 @@ class IPCFeature final : public nsIUrlCl
                  bool* aShouldContinue) override {
     NS_ENSURE_ARG_POINTER(aShouldContinue);
     *aShouldContinue = true;
 
     // Nothing to do here.
     return NS_OK;
   }
 
+  NS_IMETHOD
+  GetURIByListType(nsIChannel* aChannel,
+                   nsIUrlClassifierFeature::listType aListType,
+                   nsIURI** aURI) override {
+    NS_ENSURE_ARG_POINTER(aURI);
+
+    // This method should not be called, but we have a URI, let's return it.
+    nsCOMPtr<nsIURI> uri = mURI;
+    uri.forget(aURI);
+    return NS_OK;
+  }
+
  private:
   ~IPCFeature() = default;
 
+  nsCOMPtr<nsIURI> mURI;
   IPCURLClassifierFeature mIPCFeature;
 };
 
 NS_IMPL_ISUPPORTS(IPCFeature, nsIUrlClassifierFeature)
 
 }  // namespace
 
 NS_IMPL_ISUPPORTS(URLClassifierLocalParent, nsIUrlClassifierFeatureCallback)
@@ -125,17 +138,17 @@ mozilla::ipc::IPCResult URLClassifierLoc
       do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     OnClassifyComplete(nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>());
     return IPC_OK();
   }
 
   nsTArray<RefPtr<nsIUrlClassifierFeature>> features;
   for (const IPCURLClassifierFeature& feature : aFeatures) {
-    features.AppendElement(new IPCFeature(feature));
+    features.AppendElement(new IPCFeature(aURI, feature));
   }
 
   // Doesn't matter if we pass blacklist, whitelist or any other list.
   // IPCFeature returns always the same values.
   rv = uriClassifier->AsyncClassifyLocalWithFeatures(
       aURI, features, nsIUrlClassifierFeature::blacklist, this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     OnClassifyComplete(nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>());
@@ -149,15 +162,17 @@ NS_IMETHODIMP
 URLClassifierLocalParent::OnClassifyComplete(
     const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aResults) {
   nsTArray<URLClassifierLocalResult> ipcResults;
   for (nsIUrlClassifierFeatureResult* result : aResults) {
     URLClassifierLocalResult* ipcResult = ipcResults.AppendElement();
 
     net::UrlClassifierFeatureResult* r =
         static_cast<net::UrlClassifierFeatureResult*>(result);
+
+    ipcResult->uri() = r->URI();
     r->Feature()->GetName(ipcResult->featureName());
     ipcResult->matchingList() = r->List();
   }
 
   Unused << Send__delete__(this, ipcResults);
   return NS_OK;
 }
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -511,18 +511,16 @@ partial interface Document {
 partial interface Document {
   [Func="IsChromeOrXBL"] readonly attribute boolean hasScriptsBlockedBySandbox;
   [Func="IsChromeOrXBL"] readonly attribute boolean inlineScriptAllowedByCSP;
 };
 
 // For more information on Flash classification, see
 // toolkit/components/url-classifier/flash-block-lists.rst
 enum FlashClassification {
-  "unclassified",   // Denotes a classification that has not yet been computed.
-                    // Allows for lazy classification.
   "unknown",        // Site is not on the whitelist or blacklist
   "allowed",        // Site is on the Flash whitelist
   "denied"          // Site is on the Flash blacklist
 };
 partial interface Document {
   [ChromeOnly]
   readonly attribute FlashClassification documentFlashClassification;
 };
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -915,23 +915,23 @@ static BlobItemData* GetBlobItemDataForG
 void Grouper::PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem,
                                  const IntRect& aItemBounds,
                                  nsDisplayList* aChildren, gfxContext* aContext,
                                  WebRenderDrawEventRecorder* aRecorder) {
   switch (aItem->GetType()) {
     case DisplayItemType::TYPE_TRANSFORM: {
       DisplayItemClip currentClip = aItem->GetClip();
 
-      gfx::Matrix matrix;
+      gfxContextMatrixAutoSaveRestore saveMatrix;
       if (currentClip.HasClip()) {
         aContext->Save();
         currentClip.ApplyTo(aContext, this->mAppUnitsPerDevPixel);
         aContext->GetDrawTarget()->FlushItem(aItemBounds);
       } else {
-        matrix = aContext->CurrentMatrix();
+        saveMatrix.SetContext(aContext);
       }
 
       auto transformItem = static_cast<nsDisplayTransform*>(aItem);
       Matrix4x4Flagged trans = transformItem->GetTransform();
       Matrix trans2d;
       if (!trans.Is2D(&trans2d)) {
         // We don't currently support doing invalidation inside 3d transforms.
         // For now just paint it as a single item.
@@ -941,23 +941,21 @@ void Grouper::PaintContainerItem(DIGroup
           data->mLayerManager->EndTransaction(
               FrameLayerBuilder::DrawPaintedLayer, mDisplayListBuilder);
           aContext->GetDrawTarget()->FlushItem(aItemBounds);
         }
       } else {
         aContext->Multiply(ThebesMatrix(trans2d));
         aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext,
                                aRecorder);
+      }
 
-        if (currentClip.HasClip()) {
-          aContext->Restore();
-          aContext->GetDrawTarget()->FlushItem(aItemBounds);
-        } else {
-          aContext->SetMatrix(matrix);
-        }
+      if (currentClip.HasClip()) {
+        aContext->Restore();
+        aContext->GetDrawTarget()->FlushItem(aItemBounds);
       }
       break;
     }
     case DisplayItemType::TYPE_OPACITY: {
       auto opacityItem = static_cast<nsDisplayOpacity*>(aItem);
       float opacity = opacityItem->GetOpacity();
       if (opacity == 0.0f) {
         return;
--- a/ipc/chromium/src/base/time.h
+++ b/ipc/chromium/src/base/time.h
@@ -23,22 +23,16 @@
 
 #ifndef BASE_TIME_H_
 #define BASE_TIME_H_
 
 #include <time.h>
 
 #include "base/basictypes.h"
 
-#if defined(OS_WIN)
-// For FILETIME in FromFileTime, until it moves to a new converter class.
-// See TODO(iyengar) below.
-#include <windows.h>
-#endif
-
 namespace base {
 
 class Time;
 class TimeTicks;
 
 // This unit test does a lot of manual time manipulation.
 class PageLoadTrackerUnitTest;
 
@@ -194,21 +188,16 @@ class Time {
   static Time FromTimeT(time_t tt);
   time_t ToTimeT() const;
 
   // Converts time to/from a double which is the number of seconds since epoch
   // (Jan 1, 1970).  Webkit uses this format to represent time.
   static Time FromDoubleT(double dt);
   double ToDoubleT() const;
 
-#if defined(OS_WIN)
-  static Time FromFileTime(FILETIME ft);
-  FILETIME ToFileTime() const;
-#endif
-
   // Converts an exploded structure representing either the local time or UTC
   // into a Time class.
   static Time FromUTCExploded(const Exploded& exploded) {
     return FromExploded(false, exploded);
   }
   static Time FromLocalExploded(const Exploded& exploded) {
     return FromExploded(true, exploded);
   }
@@ -386,21 +375,16 @@ class TimeTicks {
   friend class PageLoadTrackerUnitTest;
 
   // Please use Now() to create a new object. This is for internal use
   // and testing. Ticks is in microseconds.
   explicit TimeTicks(int64_t ticks) : ticks_(ticks) {}
 
   // Tick count in microseconds.
   int64_t ticks_;
-
-#if defined(OS_WIN)
-  typedef DWORD (*TickFunctionType)(void);
-  static TickFunctionType SetMockTickFunction(TickFunctionType ticker);
-#endif
 };
 
 inline TimeTicks TimeDelta::operator+(TimeTicks t) const {
   return TimeTicks(t.ticks_ + delta_);
 }
 
 }  // namespace base
 
--- a/ipc/chromium/src/base/time_win.cc
+++ b/ipc/chromium/src/base/time_win.cc
@@ -134,27 +134,16 @@ Time Time::Now() {
 // static
 Time Time::NowFromSystemTime() {
   // Force resync.
   InitializeClock();
   return Time(initial_time);
 }
 
 // static
-Time Time::FromFileTime(FILETIME ft) {
-  return Time(FileTimeToMicroseconds(ft));
-}
-
-FILETIME Time::ToFileTime() const {
-  FILETIME utc_ft;
-  MicrosecondsToFileTime(us_, &utc_ft);
-  return utc_ft;
-}
-
-// static
 Time Time::FromExploded(bool is_local, const Exploded& exploded) {
   // Create the system struct representing our exploded time. It will either be
   // in local time or UTC.
   SYSTEMTIME st;
   st.wYear = exploded.year;
   st.wMonth = exploded.month;
   st.wDayOfWeek = exploded.day_of_week;
   st.wDay = exploded.day_of_month;
@@ -252,19 +241,11 @@ class NowSingleton {
   DWORD last_seen_;  // The last timeGetTime value we saw, to detect rollover.
 
   DISALLOW_COPY_AND_ASSIGN(NowSingleton);
 };
 
 }  // namespace
 
 // static
-TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction(
-    TickFunctionType ticker) {
-  TickFunctionType old = tick_function;
-  tick_function = ticker;
-  return old;
-}
-
-// static
 TimeTicks TimeTicks::Now() {
   return TimeTicks() + NowSingleton::instance().Now();
 }
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -857,18 +857,16 @@ description = bug 1475898 this could be 
 [PContent::LoadPlugin]
 description =
 [PContent::ConnectPluginBridge]
 description =
 [PContent::IsSecureURI]
 description =
 [PContent::PURLClassifier]
 description =
-[PContent::ClassifyLocal]
-description =
 [PContent::GetGfxVars]
 description =
 [PContent::ReadFontList]
 description =
 [PContent::GetClipboard]
 description =
 [PContent::ClipboardHasType]
 description =
--- a/js/src/wasm/cranelift/Cargo.toml
+++ b/js/src/wasm/cranelift/Cargo.toml
@@ -3,20 +3,20 @@ name = "baldrdash"
 version = "0.1.0"
 authors = ["The Spidermonkey and Cranelift developers"]
 
 [lib]
 crate-type = ["rlib"]
 name = "baldrdash"
 
 [dependencies]
-cranelift-codegen = "0.25.0"
-cranelift-wasm = "0.25.0"
+cranelift-codegen = "0.26.0"
+cranelift-wasm = "0.26.0"
 target-lexicon = "0.2.0"
-log = { version = "0.4.4", default-features = false, features = ["release_max_level_info"] }
+log = { version = "0.4.5", default-features = false, features = ["release_max_level_info"] }
 env_logger = "0.5.6"
 
 [build-dependencies]
 bindgen = {version = "0.43", default-features = false} # disable `logging` to reduce code size
 
 # Uncomment this to enable perf support in release mode.
 #[profile.release]
 #debug = true
--- a/js/src/wasm/cranelift/build.rs
+++ b/js/src/wasm/cranelift/build.rs
@@ -43,17 +43,18 @@ fn main() {
         .whitelist_type("BD_.*|Trap|TypeCode|FuncTypeIdDescKind")
         .header("baldrapi.h")
         .clang_args(&[
             "-x",
             "c++",
             "-std=gnu++14",
             "-fno-sized-deallocation",
             "-DRUST_BINDGEN",
-        ]).clang_arg("-I../..");
+        ])
+        .clang_arg("-I../..");
 
     match env::var_os("MOZ_TOPOBJDIR") {
         Some(path) => {
             let path = PathBuf::from(path).join("js/src/rust/extra-bindgen-flags");
 
             let mut extra_flags = String::new();
             File::open(&path)
                 .expect("Failed to open extra-bindgen-flags file")
--- a/js/src/wasm/cranelift/src/lib.rs
+++ b/js/src/wasm/cranelift/src/lib.rs
@@ -16,21 +16,21 @@
 extern crate cranelift_codegen;
 extern crate cranelift_wasm;
 #[macro_use]
 extern crate target_lexicon;
 #[macro_use]
 extern crate log;
 extern crate env_logger;
 
-mod baldrapi;  // Low-level C API, ignore this.
+mod baldrapi; // Low-level C API, ignore this.
 mod baldrdash; // High-level Rust API, use this.
-mod compile;   // Cranelift function compiler.
-mod cpu;       // CPU detection and `TargetISA` configuration.
-mod utils;     // Helpers for other source files.
+mod compile; // Cranelift function compiler.
+mod cpu; // CPU detection and `TargetISA` configuration.
+mod utils; // Helpers for other source files.
 mod wasm2clif; // WebAssembly to Cranelift translation callbacks.
 
 use baldrdash::{CompiledFunc, FuncCompileInput, ModuleEnvironment, StaticEnvironment};
 use compile::BatchCompiler;
 use std::ptr;
 
 #[no_mangle]
 pub extern "C" fn cranelift_initialize() {
--- a/js/src/wasm/cranelift/src/wasm2clif.rs
+++ b/js/src/wasm/cranelift/src/wasm2clif.rs
@@ -57,16 +57,21 @@ fn offset32(offset: usize) -> ir::immedi
     (offset as i32).into()
 }
 
 /// Convert a usize offset into a `Imm64` for an iadd_imm.
 fn imm64(offset: usize) -> ir::immediates::Imm64 {
     (offset as i64).into()
 }
 
+/// Convert a usize offset into a `Uimm64`.
+fn uimm64(offset: usize) -> ir::immediates::Uimm64 {
+    (offset as u64).into()
+}
+
 /// Initialize a `Signature` from a wasm signature.
 fn init_sig_from_wsig(sig: &mut ir::Signature, wsig: bd::FuncTypeWithId) {
     sig.clear(CallConv::Baldrdash);
     for &arg in wsig.args() {
         sig.params.push(ir::AbiParam::new(arg.into()));
     }
 
     if let Some(ret_type) = wsig.ret_type().into() {
@@ -296,27 +301,29 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> {
         if self.cx_addr.is_none() {
             let vmctx = self.get_vmctx_gv(&mut pos.func);
             self.cx_addr = pos
                 .func
                 .create_global_value(ir::GlobalValueData::IAddImm {
                     base: vmctx,
                     offset: imm64(self.static_env.cxTlsOffset),
                     global_type: native_pointer_type(),
-                }).into();
+                })
+                .into();
         }
         if self.realm_addr.is_none() {
             let vmctx = self.get_vmctx_gv(&mut pos.func);
             self.realm_addr = pos
                 .func
                 .create_global_value(ir::GlobalValueData::IAddImm {
                     base: vmctx,
                     offset: imm64(self.static_env.realmTlsOffset),
                     global_type: native_pointer_type(),
-                }).into();
+                })
+                .into();
         }
 
         let ptr = native_pointer_type();
         let mut flags = ir::MemFlags::new();
         flags.set_aligned();
         flags.set_notrap();
 
         let cx_addr_val = pos.ins().global_value(ptr, self.cx_addr.unwrap());
@@ -384,83 +391,79 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra
     fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable {
         let global = self.env.global(index);
         if global.is_constant() {
             // Constant globals have a known value at compile time. We insert an instruction to
             // materialize the constant at the front of the entry block.
             let mut pos = FuncCursor::new(func);
             pos.next_ebb().expect("empty function");
             pos.next_inst();
-            GlobalVariable::Const(global.emit_constant(&mut pos))
-        } else {
-            // This is a global variable. Here we don't care if it is mutable or not.
-            let offset = global.tls_offset();
-            let mut gv = self.get_vmctx_gv(func);
+            return GlobalVariable::Const(global.emit_constant(&mut pos));
+        }
+
+        // This is a global variable. Here we don't care if it is mutable or not.
+        let vmctx_gv = self.get_vmctx_gv(func);
+        let offset = global.tls_offset();
 
-            // Some globals are represented as a pointer to the actual data, in which case we
-            // must do an extra dereference to get to them.
-            if global.is_indirect() {
-                gv = func.create_global_value(ir::GlobalValueData::Load {
-                    base: gv,
-                    offset: offset32(offset),
-                    global_type: native_pointer_type(),
-                    readonly: false,
-                });
-            } else {
-                gv = func.create_global_value(ir::GlobalValueData::IAddImm {
-                    base: gv,
-                    offset: imm64(offset),
-                    global_type: native_pointer_type(),
-                });
-            }
+        // Some globals are represented as a pointer to the actual data, in which case we
+        // must do an extra dereference to get to them.
+        let (base_gv, offset) = if global.is_indirect() {
+            let gv = func.create_global_value(ir::GlobalValueData::Load {
+                base: vmctx_gv,
+                offset: offset32(offset),
+                global_type: native_pointer_type(),
+                readonly: false,
+            });
+            (gv, 0.into())
+        } else {
+            (vmctx_gv, offset32(offset))
+        };
 
-            // Create a Cranelift global variable. We don't need to remember the reference, the
-            // function translator does that for us.
-            GlobalVariable::Memory {
-                gv,
-                ty: global.value_type().into(),
-            }
+        GlobalVariable::Memory {
+            gv: base_gv,
+            ty: global.value_type().into(),
+            offset,
         }
     }
 
     fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap {
         assert_eq!(index.index(), 0, "Only one WebAssembly memory supported");
         // Get the address of the `TlsData::memoryBase` field.
         let base_addr = self.get_vmctx_gv(func);
         // Get the `TlsData::memoryBase` field. We assume this is never modified during execution
         // of the function.
         let base = func.create_global_value(ir::GlobalValueData::Load {
             base: base_addr,
             offset: offset32(0),
             global_type: native_pointer_type(),
             readonly: true,
         });
-        let min_size = ir::immediates::Imm64::new(self.env.min_memory_length());
-        let guard_size = imm64(self.static_env.memoryGuardSize);
+        let min_size = ir::immediates::Uimm64::new(self.env.min_memory_length() as u64);
+        let guard_size = uimm64(self.static_env.memoryGuardSize);
 
         let bound = self.static_env.staticMemoryBound;
         let style = if bound > 0 {
             // We have a static heap.
-            let bound = (bound as i64).into();
+            let bound = (bound as u64).into();
             ir::HeapStyle::Static { bound }
         } else {
             // Get the `TlsData::boundsCheckLimit` field.
             let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
                 base: base_addr,
                 offset: native_pointer_size().into(),
                 global_type: ir::types::I32,
                 readonly: false,
             });
             ir::HeapStyle::Dynamic { bound_gv }
         };
 
         func.create_heap(ir::HeapData {
             base,
             min_size,
-            guard_size,
+            offset_guard_size: guard_size,
             style,
             index_type: ir::types::I32,
         })
     }
 
     fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
         let mut sigdata = ir::Signature::new(CallConv::Baldrdash);
         let wsig = self.env.signature(index);
@@ -493,19 +496,19 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra
             base: table_desc.global,
             offset: offset32(native_pointer_size() as usize),
             global_type: native_pointer_type(),
             readonly: false,
         });
 
         func.create_table(ir::TableData {
             base_gv,
-            min_size: ir::immediates::Imm64::new(0),
+            min_size: ir::immediates::Uimm64::new(0),
             bound_gv,
-            element_size: ir::immediates::Imm64::new(i64::from(self.pointer_bytes()) * 2),
+            element_size: ir::immediates::Uimm64::new(u64::from(self.pointer_bytes()) * 2),
             index_type: ir::types::I32,
         })
     }
 
     fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef {
         // Create a signature.
         let mut sigdata = ir::Signature::new(CallConv::Baldrdash);
         init_sig(&mut sigdata, &self.env, index);
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1936,16 +1936,32 @@ VARCACHE_PREF(
 VARCACHE_PREF(
   "dom.security.featurePolicy.webidl.enabled",
    dom_security_featurePolicy_webidl_enabled,
   bool, PREF_VALUE
 )
 #undef PREF_VALUE
 
 //---------------------------------------------------------------------------
+// Plugins prefs
+//---------------------------------------------------------------------------
+
+VARCACHE_PREF(
+  "plugins.flashBlock.enabled",
+   plugins_flashBlock_enabled,
+  bool, false
+)
+
+VARCACHE_PREF(
+  "plugins.http_https_only",
+   plugins_http_https_only,
+  bool, true
+)
+
+//---------------------------------------------------------------------------
 // Reporting API
 //---------------------------------------------------------------------------
 
 #ifdef NIGHTLY_BUILD
 # define PREF_VALUE true
 #else
 # define PREF_VALUE false
 #endif
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5633,19 +5633,16 @@ pref("urlclassifier.blockedTable", "test
 pref("urlclassifier.flashAllowTable", "allow-flashallow-digest256");
 pref("urlclassifier.flashAllowExceptTable", "except-flashallow-digest256");
 pref("urlclassifier.flashTable", "block-flash-digest256");
 pref("urlclassifier.flashExceptTable", "except-flash-digest256");
 pref("urlclassifier.flashSubDocTable", "block-flashsubdoc-digest256");
 pref("urlclassifier.flashSubDocExceptTable", "except-flashsubdoc-digest256");
 pref("urlclassifier.flashInfobarTable", "except-flashinfobar-digest256");
 
-pref("plugins.http_https_only", true);
-pref("plugins.flashBlock.enabled", false);
-
 // Turn off Spatial navigation by default.
 pref("snav.enabled", false);
 
 // Wakelock is disabled by default.
 pref("dom.wakelock.enabled", false);
 
 // The URL of the Firefox Accounts auth server backend
 pref("identity.fxaccounts.auth.uri", "https://api.accounts.firefox.com/v1");
--- a/netwerk/base/SimpleChannelParent.cpp
+++ b/netwerk/base/SimpleChannelParent.cpp
@@ -48,16 +48,23 @@ SimpleChannelParent::NotifyTrackingCooki
 
 NS_IMETHODIMP
 SimpleChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
+SimpleChannelParent::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  // Nothing to do.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 SimpleChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
                                               const nsACString& aProvider,
                                               const nsACString& aPrefix) {
   // nothing to do
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/nsIParentChannel.idl
+++ b/netwerk/base/nsIParentChannel.idl
@@ -1,13 +1,14 @@
 /* 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 "nsIStreamListener.idl"
+#include "nsIHttpChannel.idl"
 
 interface nsITabParent;
 
 %{C++
 namespace mozilla {
 namespace net {
 class HttpChannelParentListener;
 }
@@ -41,16 +42,21 @@ interface nsIParentChannel : nsIStreamLi
   [noscript] void notifyCookieAllowed();
 
   /**
    * Called to notify the HttpChannelChild that cookie has been blocked for
    * this load.
    */
   [noscript] void notifyTrackingCookieBlocked(in uint32_t aRejectedReason);
 
+  /**
+   * Called to notify the HttpChannelChild that flash plugin state has changed.
+   */
+  [noscript] void notifyFlashPluginStateChanged(in nsIHttpChannel_FlashPluginState aState);
+
    /**
    * Called to set matched information when URL matches SafeBrowsing list.
    * @param aList
    *        Name of the list that matched
    * @param aProvider
    *        Name of provider that matched
    * @param aFullHash
    *        String represents full hash that matched
--- a/netwerk/ipc/NeckoMessageUtils.h
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -5,16 +5,17 @@
 
 #ifndef mozilla_net_NeckoMessageUtils_h
 #define mozilla_net_NeckoMessageUtils_h
 
 #include "mozilla/DebugOnly.h"
 
 #include "ipc/IPCMessageUtils.h"
 #include "nsExceptionHandler.h"
+#include "nsIHttpChannel.h"
 #include "nsPrintfCString.h"
 #include "nsString.h"
 #include "prio.h"
 #include "mozilla/net/DNS.h"
 #include "TimingStruct.h"
 
 namespace IPC {
 
@@ -174,11 +175,17 @@ struct ParamTraits<mozilla::net::Resourc
            ReadParam(aMsg, aIter, &aResult->transferSize) &&
            ReadParam(aMsg, aIter, &aResult->encodedBodySize) &&
            ReadParam(aMsg, aIter, &aResult->protocolVersion) &&
            ReadParam(aMsg, aIter, &aResult->cacheReadStart) &&
            ReadParam(aMsg, aIter, &aResult->cacheReadEnd);
   }
 };
 
+template <>
+struct ParamTraits<nsIHttpChannel::FlashPluginState>
+    : public ContiguousEnumSerializerInclusive<
+          nsIHttpChannel::FlashPluginState, nsIHttpChannel::FlashPluginUnknown,
+          nsIHttpChannel::FlashPluginLastValue> {};
+
 }  // namespace IPC
 
 #endif  // mozilla_net_NeckoMessageUtils_h
--- a/netwerk/protocol/data/DataChannelParent.cpp
+++ b/netwerk/protocol/data/DataChannelParent.cpp
@@ -48,16 +48,23 @@ DataChannelParent::NotifyTrackingCookieB
 
 NS_IMETHODIMP
 DataChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
+DataChannelParent::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  // Nothing to do.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 DataChannelParent::SetClassifierMatchedInfo(const nsACString &aList,
                                             const nsACString &aProvider,
                                             const nsACString &aFullHash) {
   // nothing to do
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/file/FileChannelParent.cpp
+++ b/netwerk/protocol/file/FileChannelParent.cpp
@@ -48,16 +48,23 @@ FileChannelParent::NotifyTrackingCookieB
 
 NS_IMETHODIMP
 FileChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
+FileChannelParent::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  // Nothing to do.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 FileChannelParent::SetClassifierMatchedInfo(const nsACString &aList,
                                             const nsACString &aProvider,
                                             const nsACString &aFullHash) {
   // nothing to do
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -519,16 +519,23 @@ FTPChannelParent::NotifyTrackingCookieBl
 
 NS_IMETHODIMP
 FTPChannelParent::NotifyTrackingResource(bool aIsThirdParty) {
   // One day, this should probably be filled in.
   return NS_OK;
 }
 
 NS_IMETHODIMP
+FTPChannelParent::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  // One day, this should probably be filled in.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 FTPChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
                                            const nsACString& aProvider,
                                            const nsACString& aFullHash) {
   // One day, this should probably be filled in.
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
@@ -353,16 +353,35 @@ IPCResult HttpBackgroundChannelChild::Re
 
   // NotifyTrackingResource has no order dependency to OnStartRequest.
   // It this be handled as soon as possible
   mChannelChild->ProcessNotifyTrackingResource(aIsThirdParty);
 
   return IPC_OK();
 }
 
+IPCResult HttpBackgroundChannelChild::RecvNotifyFlashPluginStateChanged(
+    const nsIHttpChannel::FlashPluginState& aState) {
+  LOG(
+      ("HttpBackgroundChannelChild::RecvNotifyFlashPluginStateChanged "
+       "[this=%p]\n",
+       this));
+  MOZ_ASSERT(OnSocketThread());
+
+  if (NS_WARN_IF(!mChannelChild)) {
+    return IPC_OK();
+  }
+
+  // NotifyFlashPluginStateChanged has no order dependency to OnStartRequest.
+  // It this be handled as soon as possible
+  mChannelChild->ProcessNotifyFlashPluginStateChanged(aState);
+
+  return IPC_OK();
+}
+
 IPCResult HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo(
     const ClassifierInfo& info) {
   LOG(("HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n",
        this));
   MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.h
@@ -67,16 +67,19 @@ class HttpBackgroundChannelChild final :
 
   IPCResult RecvNotifyCookieAllowed() override;
 
   IPCResult RecvNotifyTrackingCookieBlocked(
       const uint32_t& aRejectedReason) override;
 
   IPCResult RecvNotifyTrackingResource(const bool& aIsThirdParty) override;
 
+  IPCResult RecvNotifyFlashPluginStateChanged(
+      const nsIHttpChannel::FlashPluginState& aState) override;
+
   IPCResult RecvSetClassifierMatchedInfo(const ClassifierInfo& info) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
  private:
   virtual ~HttpBackgroundChannelChild();
 
   // Initiate the creation of the PBckground IPC channel.
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
@@ -421,16 +421,47 @@ bool HttpBackgroundChannelParent::OnNoti
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
 
     return NS_SUCCEEDED(rv);
   }
 
   return SendNotifyTrackingResource(aIsThirdParty);
 }
 
+bool HttpBackgroundChannelParent::OnNotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  LOG(
+      ("HttpBackgroundChannelParent::OnNotifyFlashPluginStateChanged "
+       "[this=%p]\n",
+       this));
+  AssertIsInMainProcess();
+
+  if (NS_WARN_IF(!mIPCOpened)) {
+    return false;
+  }
+
+  if (!IsOnBackgroundThread()) {
+    MutexAutoLock lock(mBgThreadMutex);
+    RefPtr<HttpBackgroundChannelParent> self = this;
+    nsresult rv = mBackgroundThread->Dispatch(
+        NS_NewRunnableFunction(
+            "net::HttpBackgroundChannelParent::OnNotifyFlashPluginStateChanged",
+            [self, aState]() {
+              self->OnNotifyFlashPluginStateChanged(aState);
+            }),
+        NS_DISPATCH_NORMAL);
+
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+    return NS_SUCCEEDED(rv);
+  }
+
+  return SendNotifyFlashPluginStateChanged(aState);
+}
+
 bool HttpBackgroundChannelParent::OnSetClassifierMatchedInfo(
     const nsACString& aList, const nsACString& aProvider,
     const nsACString& aFullHash) {
   LOG(("HttpBackgroundChannelParent::OnSetClassifierMatchedInfo [this=%p]\n",
        this));
   AssertIsInMainProcess();
 
   if (NS_WARN_IF(!mIPCOpened)) {
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.h
@@ -70,16 +70,19 @@ class HttpBackgroundChannelParent final 
   bool OnNotifyCookieAllowed();
 
   // To send NotifyTrackingCookieBlocked message over background channel.
   bool OnNotifyTrackingCookieBlocked(uint32_t aRejectedReason);
 
   // To send NotifyTrackingResource message over background channel.
   bool OnNotifyTrackingResource(bool aIsThirdParty);
 
+  // To send NotifyFlashPluginStateChanged message over background channel.
+  bool OnNotifyFlashPluginStateChanged(nsIHttpChannel::FlashPluginState aState);
+
   // To send SetClassifierMatchedInfo message over background channel.
   bool OnSetClassifierMatchedInfo(const nsACString& aList,
                                   const nsACString& aProvider,
                                   const nsACString& aFullHash);
 
  protected:
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -163,16 +163,17 @@ HttpBaseChannel::HttpBaseChannel()
       mTopLevelOuterContentWindowId(0),
       mAltDataLength(0),
       mChannelId(0),
       mReqContentLength(0U),
       mStatus(NS_OK),
       mCanceled(false),
       mIsFirstPartyTrackingResource(false),
       mIsThirdPartyTrackingResource(false),
+      mFlashPluginState(nsIHttpChannel::FlashPluginUnknown),
       mLoadFlags(LOAD_NORMAL),
       mCaps(0),
       mClassOfService(0),
       mUpgradeToSecure(false),
       mApplyConversion(true),
       mIsPending(false),
       mWasOpened(false),
       mRequestObserversCalled(false),
@@ -314,16 +315,22 @@ void HttpBaseChannel::SetIsTrackingResou
     MOZ_ASSERT(!mIsFirstPartyTrackingResource);
     mIsThirdPartyTrackingResource = true;
   } else {
     MOZ_ASSERT(!mIsThirdPartyTrackingResource);
     mIsFirstPartyTrackingResource = true;
   }
 }
 
+void HttpBaseChannel::SetFlashPluginState(
+    nsIHttpChannel::FlashPluginState aState) {
+  LOG(("HttpBaseChannel::SetFlashPluginState %p", this));
+  mFlashPluginState = aState;
+}
+
 nsresult HttpBaseChannel::Init(nsIURI* aURI, uint32_t aCaps,
                                nsProxyInfo* aProxyInfo,
                                uint32_t aProxyResolveFlags, nsIURI* aProxyURI,
                                uint64_t aChannelId) {
   LOG1(("HttpBaseChannel::Init [this=%p]\n", this));
 
   MOZ_ASSERT(aURI, "null uri");
 
@@ -1481,16 +1488,23 @@ HttpBaseChannel::GetIsTrackingResource(b
 NS_IMETHODIMP
 HttpBaseChannel::GetIsThirdPartyTrackingResource(bool* aIsTrackingResource) {
   MOZ_ASSERT(!(mIsFirstPartyTrackingResource && mIsThirdPartyTrackingResource));
   *aIsTrackingResource = mIsThirdPartyTrackingResource;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HttpBaseChannel::GetFlashPluginState(nsIHttpChannel::FlashPluginState* aState) {
+  uint32_t flashPluginState = mFlashPluginState;
+  *aState = (nsIHttpChannel::FlashPluginState)flashPluginState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 HttpBaseChannel::OverrideTrackingFlagsForDocumentCookieAccessor(
     nsIHttpChannel* aDocumentChannel) {
   LOG(
       ("HttpBaseChannel::OverrideTrackingFlagsForDocumentCookieAccessor() %p "
        "mIsFirstPartyTrackingResource=%d  mIsThirdPartyTrackingResource=%d",
        this, static_cast<int>(mIsFirstPartyTrackingResource),
        static_cast<int>(mIsThirdPartyTrackingResource)));
 
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -233,16 +233,18 @@ class HttpBaseChannel : public nsHashPro
   NS_IMETHOD SetTopLevelContentWindowId(uint64_t aContentWindowId) override;
   NS_IMETHOD GetTopLevelOuterContentWindowId(uint64_t *aWindowId) override;
   NS_IMETHOD SetTopLevelOuterContentWindowId(uint64_t aWindowId) override;
   NS_IMETHOD GetIsTrackingResource(bool *aIsTrackingResource) override;
   NS_IMETHOD GetIsThirdPartyTrackingResource(
       bool *aIsTrackingResource) override;
   NS_IMETHOD OverrideTrackingFlagsForDocumentCookieAccessor(
       nsIHttpChannel *aDocumentChannel) override;
+  NS_IMETHOD GetFlashPluginState(
+      nsIHttpChannel::FlashPluginState *aState) override;
 
   // nsIHttpChannelInternal
   NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI) override;
   NS_IMETHOD SetDocumentURI(nsIURI *aDocumentURI) override;
   NS_IMETHOD GetRequestVersion(uint32_t *major, uint32_t *minor) override;
   NS_IMETHOD GetResponseVersion(uint32_t *major, uint32_t *minor) override;
   NS_IMETHOD SetCookie(const char *aCookieHeader) override;
   NS_IMETHOD GetThirdPartyFlags(uint32_t *aForce) override;
@@ -408,16 +410,18 @@ class HttpBaseChannel : public nsHashPro
 
   // Callback on STS thread called by CopyComplete when NS_AsyncCopy()
   // is finished. This function works as a proxy function to dispatch
   // |EnsureUploadStreamIsCloneableComplete| to main thread.
   virtual void OnCopyComplete(nsresult aStatus);
 
   void SetIsTrackingResource(bool aIsThirdParty);
 
+  void SetFlashPluginState(nsIHttpChannel::FlashPluginState aState);
+
   const uint64_t &ChannelId() const { return mChannelId; }
 
   void InternalSetUploadStream(nsIInputStream *uploadStream) {
     mUploadStream = uploadStream;
   }
 
   void InternalSetUploadStreamLength(uint64_t aLength) {
     mReqContentLength = aLength;
@@ -642,16 +646,17 @@ class HttpBaseChannel : public nsHashPro
 
   Atomic<nsresult, ReleaseAcquire> mStatus;
 
   // Use Release-Acquire ordering to ensure the OMT ODA is ignored while channel
   // is canceled on main thread.
   Atomic<bool, ReleaseAcquire> mCanceled;
   Atomic<bool, ReleaseAcquire> mIsFirstPartyTrackingResource;
   Atomic<bool, ReleaseAcquire> mIsThirdPartyTrackingResource;
+  Atomic<uint32_t, ReleaseAcquire> mFlashPluginState;
 
   uint32_t mLoadFlags;
   uint32_t mCaps;
   uint32_t mClassOfService;
 
   uint32_t mUpgradeToSecure : 1;
   uint32_t mApplyConversion : 1;
   uint32_t mIsPending : 1;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1857,16 +1857,25 @@ void HttpChannelChild::ProcessNotifyTrac
       ("HttpChannelChild::ProcessNotifyTrackingResource thirdparty=%d "
        "[this=%p]\n",
        static_cast<int>(aIsThirdParty), this));
   MOZ_ASSERT(OnSocketThread());
 
   SetIsTrackingResource(aIsThirdParty);
 }
 
+void HttpChannelChild::ProcessNotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  LOG(("HttpChannelChild::ProcessNotifyFlashPluginStateChanged [this=%p]\n",
+       this));
+  MOZ_ASSERT(OnSocketThread());
+
+  SetFlashPluginState(aState);
+}
+
 void HttpChannelChild::FlushedForDiversion() {
   LOG(("HttpChannelChild::FlushedForDiversion [this=%p]\n", this));
   MOZ_RELEASE_ASSERT(mDivertingToParent);
 
   // Once this is set, it should not be unset before HttpChannelChild is taken
   // down. After it is set, no OnStart/OnData/OnStop callbacks should be
   // received from the parent channel, nor dequeued from the ChannelEventQueue.
   mFlushedForDiversion = true;
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -260,16 +260,18 @@ class HttpChannelChild final : public PH
   void ProcessOnProgress(const int64_t& aProgress, const int64_t& aProgressMax);
   void ProcessOnStatus(const nsresult& aStatus);
   void ProcessFlushedForDiversion();
   void ProcessDivertMessages();
   void ProcessNotifyTrackingProtectionDisabled();
   void ProcessNotifyCookieAllowed();
   void ProcessNotifyTrackingCookieBlocked(uint32_t aRejectedReason);
   void ProcessNotifyTrackingResource(bool aIsThirdParty);
+  void ProcessNotifyFlashPluginStateChanged(
+      nsIHttpChannel::FlashPluginState aState);
   void ProcessSetClassifierMatchedInfo(const nsCString& aList,
                                        const nsCString& aProvider,
                                        const nsCString& aFullHash);
 
   // Return true if we need to tell the parent the size of unreported received
   // data
   bool NeedToReportBytesRead();
   int32_t mUnreportBytesRead = 0;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1822,16 +1822,27 @@ HttpChannelParent::NotifyTrackingResourc
   if (!mIPCClosed) {
     MOZ_ASSERT(mBgParent);
     Unused << mBgParent->OnNotifyTrackingResource(aIsThirdParty);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HttpChannelParent::NotifyFlashPluginStateChanged(
+    nsIHttpChannel::FlashPluginState aState) {
+  LOG(("HttpChannelParent::NotifyFlashPluginStateChanged [this=%p]\n", this));
+  if (!mIPCClosed) {
+    MOZ_ASSERT(mBgParent);
+    Unused << mBgParent->OnNotifyFlashPluginStateChanged(aState);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 HttpChannelParent::Delete() {
   if (!mIPCClosed) Unused << DoSendDeleteSelf();
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIParentRedirectingChannel
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -84,16 +84,22 @@ NullHttpChannel::SetTopLevelOuterContent
 }
 
 NS_IMETHODIMP
 NullHttpChannel::GetIsTrackingResource(bool *aIsTrackingResource) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+NullHttpChannel::GetFlashPluginState(
+    nsIHttpChannel::FlashPluginState *aResult) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 NullHttpChannel::GetIsThirdPartyTrackingResource(bool *aIsTrackingResource) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::OverrideTrackingFlagsForDocumentCookieAccessor(
     nsIHttpChannel *aDocumentChannel) {
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/netwerk/protocol/http/PHttpBackgroundChannel.ipdl
+++ b/netwerk/protocol/http/PHttpBackgroundChannel.ipdl
@@ -8,16 +8,17 @@
 include protocol PBackground;
 include NeckoChannelParams;
 include PURLClassifierInfo;
 
 include "mozilla/net/NeckoMessageUtils.h";
 
 using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
 using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
+using nsIHttpChannel::FlashPluginState from "mozilla/net/NeckoMessageUtils.h";
 
 namespace mozilla {
 namespace net {
 
 //-------------------------------------------------------------------
 async protocol PHttpBackgroundChannel
 {
   manager PBackground;
@@ -61,16 +62,20 @@ child:
 
   // Tell the child that tracking cookies are blocked for this load.
   async NotifyTrackingCookieBlocked(uint32_t aRejectedReason);
 
   // Tell the child that the resource being loaded is on the tracking
   // protection list.
   async NotifyTrackingResource(bool aIsThirdParty);
 
+  // Tell the child that the current channel's document is not allowed to load
+  // flash content.
+  async NotifyFlashPluginStateChanged(FlashPluginState aState);
+
   // Tell the child information of matched URL againts SafeBrowsing list
   async SetClassifierMatchedInfo(ClassifierInfo info);
 
   async __delete__();
 
 };
 
 
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -491,16 +491,30 @@ interface nsIHttpChannel : nsIChannel
      * protection list and is considered third-party with the top window URI.
      * This is only available if the privacy.trackingprotection.annotate_channels
      * pref is set and its value should only be relied on after the channel has
      * established a connection.
      */
     [infallible] readonly attribute boolean isThirdPartyTrackingResource;
 
     /**
+     * Returns the allowing status for flash plugin for this channel.
+     */
+    cenum FlashPluginState : 8 {
+      FlashPluginUnknown = 0,
+      FlashPluginAllowed = 1,
+      FlashPluginDenied = 2,
+      FlashPluginDeniedInSubdocuments = 3,
+
+      // Keep this equal to the last value.
+      FlashPluginLastValue = 3,
+    };
+    [infallible] readonly attribute nsIHttpChannel_FlashPluginState flashPluginState;
+
+    /**
      * This method is used by the document.cookie call site in order
      * to override the tracking status of an HTTP channel. This should
      * only be called by Gecko under certain circumstances when Gecko
      * can guarantee that the channel classifier service will not be
      * determining the tracking status of the channel.
      *
      * @param aDocumentChannel
      *        The document channel from which to copy the tracking flags
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -761,16 +761,23 @@ nsViewSourceChannel::SetTopLevelOuterCon
 NS_IMETHODIMP
 nsViewSourceChannel::GetIsTrackingResource(bool *aIsTrackingResource) {
   return !mHttpChannel
              ? NS_ERROR_NULL_POINTER
              : mHttpChannel->GetIsTrackingResource(aIsTrackingResource);
 }
 
 NS_IMETHODIMP
+nsViewSourceChannel::GetFlashPluginState(
+    nsIHttpChannel::FlashPluginState *aResult) {
+  return !mHttpChannel ? NS_ERROR_NULL_POINTER
+                       : mHttpChannel->GetFlashPluginState(aResult);
+}
+
+NS_IMETHODIMP
 nsViewSourceChannel::GetIsThirdPartyTrackingResource(
     bool *aIsTrackingResource) {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER
                        : mHttpChannel->GetIsThirdPartyTrackingResource(
                              aIsTrackingResource);
 }
 
 NS_IMETHODIMP
--- a/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp
+++ b/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp
@@ -19,16 +19,84 @@
 #include "nsServiceManagerUtils.h"
 #include "nsNetUtil.h"
 
 namespace mozilla {
 namespace net {
 
 namespace {
 
+// When we do blacklist/whitelist classification, from a list of features, we
+// need to aggregate them per URI, because not all the features work with the
+// same channel's URI.
+// This struct contains only the features able to deal with a particular URI.
+// See more in GetFeatureTasks().
+struct FeatureTask {
+  nsCOMPtr<nsIURI> mURI;
+  // Let's use RefPtr<> here, because this needs to be used with methods which
+  // require it.
+  nsTArray<RefPtr<nsIUrlClassifierFeature>> mFeatures;
+};
+
+// Features are able to classify particular URIs from a channel. For instance,
+// tracking-annotation feature uses the top-level URI to whitelist the current
+// channel's URI; flash feature always uses the channel's URI.  Because of
+// this, this function aggregates feature per URI in an array of FeatureTask
+// object.
+nsresult GetFeatureTasks(
+    nsIChannel* aChannel,
+    const nsTArray<nsCOMPtr<nsIUrlClassifierFeature>>& aFeatures,
+    nsIUrlClassifierFeature::listType aListType,
+    nsTArray<FeatureTask>& aTasks) {
+  MOZ_ASSERT(!aFeatures.IsEmpty());
+
+  // Let's unify features per nsIURI.
+  for (nsIUrlClassifierFeature* feature : aFeatures) {
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv =
+        feature->GetURIByListType(aChannel, aListType, getter_AddRefs(uri));
+    if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
+      if (UC_LOG_ENABLED()) {
+        nsAutoCString errorName;
+        GetErrorName(rv, errorName);
+        UC_LOG(
+            ("GetFeatureTasks got an unexpected error (rv=%s) while trying to "
+             "create a whitelist URI. Allowing tracker.",
+             errorName.get()));
+      }
+      return rv;
+    }
+
+    MOZ_ASSERT(uri);
+
+    bool found = false;
+    for (FeatureTask& task : aTasks) {
+      bool equal = false;
+      rv = task.mURI->Equals(uri, &equal);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      if (equal) {
+        task.mFeatures.AppendElement(feature);
+        found = true;
+        break;
+      }
+    }
+
+    if (!found) {
+      FeatureTask* task = aTasks.AppendElement();
+      task->mURI = uri;
+      task->mFeatures.AppendElement(feature);
+    }
+  }
+
+  return NS_OK;
+}
+
 nsresult TrackerFound(
     const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aResults,
     nsIChannel* aChannel, const std::function<void()>& aCallback) {
   // Let's ask the features to do the magic.
   for (nsIUrlClassifierFeatureResult* result : aResults) {
     UrlClassifierFeatureResult* r =
         static_cast<UrlClassifierFeatureResult*>(result);
 
@@ -43,137 +111,86 @@ nsresult TrackerFound(
       break;
     }
   }
 
   aCallback();
   return NS_OK;
 }
 
-nsresult CreateWhiteListURI(nsIChannel* aChannel, nsIURI** aURI) {
-  MOZ_ASSERT(aChannel);
-  MOZ_ASSERT(aURI);
-
-  nsresult rv;
-  nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!chan) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIURI> topWinURI;
-  rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!topWinURI) {
-    if (UC_LOG_ENABLED()) {
-      nsresult rv;
-      nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(aChannel, &rv);
-      nsCOMPtr<nsIURI> uri;
-      rv = httpChan->GetURI(getter_AddRefs(uri));
-      nsAutoCString spec;
-      uri->GetAsciiSpec(spec);
-      spec.Truncate(
-          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
-      UC_LOG(
-          ("CreateWhiteListURI: No window URI associated with %s", spec.get()));
-    }
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIScriptSecurityManager> securityManager =
-      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIPrincipal> chanPrincipal;
-  rv = securityManager->GetChannelURIPrincipal(aChannel,
-                                               getter_AddRefs(chanPrincipal));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Craft a whitelist URL like "toplevel.page/?resource=third.party.domain"
-  nsAutoCString pageHostname, resourceDomain;
-  rv = topWinURI->GetHost(pageHostname);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = chanPrincipal->GetBaseDomain(resourceDomain);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsAutoCString whitelistEntry = NS_LITERAL_CSTRING("http://") + pageHostname +
-                                 NS_LITERAL_CSTRING("/?resource=") +
-                                 resourceDomain;
-  UC_LOG(("CreateWhiteListURI: Looking for %s in the whitelist (channel=%p)",
-          whitelistEntry.get(), aChannel));
-
-  nsCOMPtr<nsIURI> whitelistURI;
-  rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  whitelistURI.forget(aURI);
-  return NS_OK;
-}
-
-nsresult IsTrackerWhitelisted(
-    nsIURI* aWhiteListURI,
-    const nsTArray<RefPtr<nsIUrlClassifierFeature>>& aFeatures,
-    nsIUrlClassifierFeatureCallback* aCallback) {
-  MOZ_ASSERT(aWhiteListURI);
-  MOZ_ASSERT(!aFeatures.IsEmpty());
-  MOZ_ASSERT(aCallback);
-
-  nsresult rv;
-  nsCOMPtr<nsIURIClassifier> uriClassifier =
-      do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return uriClassifier->AsyncClassifyLocalWithFeatures(
-      aWhiteListURI, aFeatures, nsIUrlClassifierFeature::whitelist, aCallback);
-}
-
 // This class is designed to get the results of checking whitelist.
 class WhitelistClassifierCallback final
     : public nsIUrlClassifierFeatureCallback {
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIURLCLASSIFIERFEATURECALLBACK
 
   WhitelistClassifierCallback(
-      nsIChannel* aChannel, nsIURI* aURI,
+      nsIChannel* aChannel,
       const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aBlacklistResults,
       std::function<void()>& aCallback)
       : mChannel(aChannel),
-        mURI(aURI),
+        mTaskCount(0),
         mBlacklistResults(aBlacklistResults),
         mChannelCallback(aCallback) {
     MOZ_ASSERT(mChannel);
-    MOZ_ASSERT(mURI);
     MOZ_ASSERT(!mBlacklistResults.IsEmpty());
   }
 
+  void SetTaskCount(uint32_t aTaskCount) {
+    MOZ_ASSERT(aTaskCount > 0);
+    mTaskCount = aTaskCount;
+  }
+
  private:
   ~WhitelistClassifierCallback() = default;
 
+  nsresult OnClassifyCompleteInternal();
+
   nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsIURI> mURI;
+  uint32_t mTaskCount;
   nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> mBlacklistResults;
   std::function<void()> mChannelCallback;
+
+  nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> mWhitelistResults;
 };
 
 NS_IMPL_ISUPPORTS(WhitelistClassifierCallback, nsIUrlClassifierFeatureCallback)
 
 NS_IMETHODIMP
 WhitelistClassifierCallback::OnClassifyComplete(
     const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aWhitelistResults) {
+  MOZ_ASSERT(mTaskCount > 0);
+
   UC_LOG(("WhitelistClassifierCallback[%p]:OnClassifyComplete channel=%p", this,
           mChannel.get()));
 
+  mWhitelistResults.AppendElements(aWhitelistResults);
+
+  if (--mTaskCount) {
+    // More callbacks will come.
+    return NS_OK;
+  }
+
+  return OnClassifyCompleteInternal();
+}
+
+nsresult WhitelistClassifierCallback::OnClassifyCompleteInternal() {
   nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> remainingResults;
 
   for (nsIUrlClassifierFeatureResult* blacklistResult : mBlacklistResults) {
-    nsIUrlClassifierFeature* blacklistFeature =
-        static_cast<UrlClassifierFeatureResult*>(blacklistResult)->Feature();
+    UrlClassifierFeatureResult* result =
+        static_cast<UrlClassifierFeatureResult*>(blacklistResult);
+
+    nsIUrlClassifierFeature* blacklistFeature = result->Feature();
     MOZ_ASSERT(blacklistFeature);
 
     bool found = false;
-    for (nsIUrlClassifierFeatureResult* whitelistResult : aWhitelistResults) {
+    for (nsIUrlClassifierFeatureResult* whitelistResult : mWhitelistResults) {
       // We can do pointer comparison because Features are singletons.
       if (static_cast<UrlClassifierFeatureResult*>(whitelistResult)
               ->Feature() == blacklistFeature) {
         found = true;
         break;
       }
     }
 
@@ -183,211 +200,260 @@ WhitelistClassifierCallback::OnClassifyC
 
     // Maybe we have to skip this host
     nsAutoCString skipList;
     nsresult rv = blacklistFeature->GetSkipHostList(skipList);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       continue;
     }
 
-    if (nsContentUtils::IsURIInList(mURI, skipList)) {
+    if (nsContentUtils::IsURIInList(result->URI(), skipList)) {
       if (UC_LOG_ENABLED()) {
-        nsCString spec = mURI->GetSpecOrDefault();
         UC_LOG(
-            ("WhitelistClassifierCallback[%p]::OnClassifyComplete uri %s found "
-             "in skiplist",
-             this, spec.get()));
+            ("WhitelistClassifierCallback[%p]::OnClassifyComplete uri found in "
+             "skiplist",
+             this));
       }
 
       continue;
     }
 
     remainingResults.AppendElement(blacklistResult);
   }
 
   // Whitelist lookup results
 
   if (remainingResults.IsEmpty()) {
     if (UC_LOG_ENABLED()) {
-      nsCString spec = mURI->GetSpecOrDefault();
-      spec.Truncate(
-          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
       UC_LOG(
-          ("WhitelistClassifierCallback[%p]::OnClassifyComplete uri %s fully "
+          ("WhitelistClassifierCallback[%p]::OnClassifyComplete uri fully "
            "whitelisted",
-           this, spec.get()));
+           this));
     }
 
     mChannelCallback();
     return NS_OK;
   }
 
   if (UC_LOG_ENABLED()) {
-    nsCString spec = mURI->GetSpecOrDefault();
-    spec.Truncate(std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
     UC_LOG(
-        ("WhitelistClassifierCallback[%p]::OnClassifyComplete "
-         "channel[%p] uri=%s, should not be whitelisted",
-         this, mChannel.get(), spec.get()));
+        ("WhitelistClassifierCallback[%p]::OnClassifyComplete channel[%p] "
+         "should not be whitelisted",
+         this, mChannel.get()));
   }
 
   return TrackerFound(remainingResults, mChannel, mChannelCallback);
 }
 
 // This class is designed to get the results of checking blacklist.
 class BlacklistClassifierCallback final
     : public nsIUrlClassifierFeatureCallback {
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIURLCLASSIFIERFEATURECALLBACK
 
-  BlacklistClassifierCallback(nsIChannel* aChannel, nsIURI* aURI,
+  BlacklistClassifierCallback(nsIChannel* aChannel,
                               std::function<void()>&& aCallback)
-      : mChannel(aChannel), mURI(aURI), mChannelCallback(std::move(aCallback)) {
+      : mChannel(aChannel),
+        mTaskCount(0),
+        mChannelCallback(std::move(aCallback)) {
     MOZ_ASSERT(mChannel);
-    MOZ_ASSERT(mURI);
+  }
+
+  void SetTaskCount(uint32_t aTaskCount) {
+    MOZ_ASSERT(aTaskCount > 0);
+    mTaskCount = aTaskCount;
   }
 
  private:
   ~BlacklistClassifierCallback() = default;
 
+  nsresult OnClassifyCompleteInternal();
+
   nsCOMPtr<nsIChannel> mChannel;
-  nsCOMPtr<nsIURI> mURI;
+  uint32_t mTaskCount;
   std::function<void()> mChannelCallback;
+
+  nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> mResults;
 };
 
 NS_IMPL_ISUPPORTS(BlacklistClassifierCallback, nsIUrlClassifierFeatureCallback)
 
 NS_IMETHODIMP
 BlacklistClassifierCallback::OnClassifyComplete(
     const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aResults) {
-  UC_LOG(("BlacklistClassifierCallback[%p]:OnClassifyComplete", this));
+  MOZ_ASSERT(mTaskCount > 0);
+
+  UC_LOG(("BlacklistClassifierCallback[%p]:OnClassifyComplete - remaining %d",
+          this, mTaskCount));
+
+  mResults.AppendElements(aResults);
 
+  if (--mTaskCount) {
+    // More callbacks will come.
+    return NS_OK;
+  }
+
+  return OnClassifyCompleteInternal();
+}
+
+nsresult BlacklistClassifierCallback::OnClassifyCompleteInternal() {
   // All good! The URL has not been classified.
-  if (aResults.IsEmpty()) {
+  if (mResults.IsEmpty()) {
     if (UC_LOG_ENABLED()) {
-      nsCString spec = mURI->GetSpecOrDefault();
-      spec.Truncate(
-          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
       UC_LOG(
-          ("BlacklistClassifierCallback[%p]::OnClassifyComplete uri %s not "
-           "found in blacklist",
-           this, spec.get()));
+          ("BlacklistClassifierCallback[%p]::OnClassifyComplete uri not found "
+           "in blacklist",
+           this));
     }
 
     mChannelCallback();
     return NS_OK;
   }
 
   if (UC_LOG_ENABLED()) {
-    nsCString spec = mURI->GetSpecOrDefault();
-    spec.Truncate(std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
     UC_LOG(
-        ("BlacklistClassifierCallback[%p]::OnClassifyComplete uri %s is in "
+        ("BlacklistClassifierCallback[%p]::OnClassifyComplete uri is in "
          "blacklist. Start checking whitelist.",
-         this, spec.get()));
+         this));
   }
 
-  nsCOMPtr<nsIURI> whitelistURI;
-  nsresult rv = CreateWhiteListURI(mChannel, getter_AddRefs(whitelistURI));
-  if (NS_FAILED(rv)) {
-    nsAutoCString errorName;
-    GetErrorName(rv, errorName);
-    NS_WARNING(
-        nsPrintfCString("BlacklistClassifierCallback[%p]:OnClassifyComplete "
-                        "got an unexpected error (rv=%s) while trying to "
-                        "create a whitelist URI. Allowing tracker.",
-                        this, errorName.get())
-            .get());
-    return TrackerFound(aResults, mChannel, mChannelCallback);
+  nsTArray<nsCOMPtr<nsIUrlClassifierFeature>> features;
+  for (nsIUrlClassifierFeatureResult* result : mResults) {
+    features.AppendElement(
+        static_cast<UrlClassifierFeatureResult*>(result)->Feature());
   }
 
-  if (!whitelistURI) {
+  nsTArray<FeatureTask> tasks;
+  nsresult rv = GetFeatureTasks(mChannel, features,
+                                nsIUrlClassifierFeature::whitelist, tasks);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return TrackerFound(mResults, mChannel, mChannelCallback);
+  }
+
+  if (tasks.IsEmpty()) {
     UC_LOG(
         ("BlacklistClassifierCallback[%p]:OnClassifyComplete could not create "
          "a whitelist URI. Ignoring whitelist.",
          this));
 
-    return TrackerFound(aResults, mChannel, mChannelCallback);
+    return TrackerFound(mResults, mChannel, mChannelCallback);
   }
 
-  nsCOMPtr<nsIUrlClassifierFeatureCallback> callback =
-      new WhitelistClassifierCallback(mChannel, mURI, aResults,
-                                      mChannelCallback);
+  RefPtr<WhitelistClassifierCallback> callback =
+      new WhitelistClassifierCallback(mChannel, mResults, mChannelCallback);
+
+  nsCOMPtr<nsIURIClassifier> uriClassifier =
+      do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t pendingCallbacks = 0;
+  for (FeatureTask& task : tasks) {
+    rv = uriClassifier->AsyncClassifyLocalWithFeatures(
+        task.mURI, task.mFeatures, nsIUrlClassifierFeature::whitelist,
+        callback);
 
-  // xpcom parser creates array of interfaces using RefPtr<>.
-  nsTArray<RefPtr<nsIUrlClassifierFeature>> refPtrFeatures;
-  for (nsIUrlClassifierFeatureResult* result : aResults) {
-    refPtrFeatures.AppendElement(
-        static_cast<UrlClassifierFeatureResult*>(result)->Feature());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      if (UC_LOG_ENABLED()) {
+        nsAutoCString errorName;
+        GetErrorName(rv, errorName);
+        UC_LOG((
+            "BlacklistClassifierCallback[%p]:OnClassifyComplete Failed "
+            "calling AsyncClassifyLocalWithFeatures with rv=%s. Let's move on.",
+            this, errorName.get()));
+      }
+
+      continue;
+    }
+
+    ++pendingCallbacks;
   }
 
-  rv = IsTrackerWhitelisted(whitelistURI, refPtrFeatures, callback);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
+  // All the AsyncClassifyLocalWithFeatures() calls return error. We do not
+  // expect callbacks.
+  if (pendingCallbacks == 0) {
     if (UC_LOG_ENABLED()) {
-      nsAutoCString errorName;
-      GetErrorName(rv, errorName);
       UC_LOG(
-          ("BlacklistClassifierCallback[%p]:OnClassifyComplete "
-           "IsTrackerWhitelisted has failed with rv=%s.",
-           this, errorName.get()));
+          ("BlacklistClassifierCallback[%p]:OnClassifyComplete All "
+           "AsyncClassifyLocalWithFeatures() calls return errors. We cannot "
+           "continue.",
+           this));
     }
 
-    return TrackerFound(aResults, mChannel, mChannelCallback);
+    return TrackerFound(mResults, mChannel, mChannelCallback);
   }
 
   // Nothing else do here. Let's wait for the WhitelistClassifierCallback.
+  callback->SetTaskCount(pendingCallbacks);
   return NS_OK;
 }
 
 }  // namespace
 
 /* static */ nsresult AsyncUrlChannelClassifier::CheckChannel(
     nsIChannel* aChannel, std::function<void()>&& aCallback) {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(aChannel);
 
   if (!aCallback) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
-  if (NS_FAILED(rv) || !uri) {
+  // We need to obtain the list of nsIUrlClassifierFeature objects able to
+  // classify this channel. If the list is empty, we do an early return.
+  nsTArray<nsCOMPtr<nsIUrlClassifierFeature>> features;
+  UrlClassifierFeatureFactory::GetFeaturesFromChannel(aChannel, features);
+  if (features.IsEmpty()) {
+    UC_LOG(
+        ("AsyncUrlChannelClassifier: Nothing to do for channel %p", aChannel));
+    return NS_ERROR_FAILURE;
+  }
+
+  nsTArray<FeatureTask> tasks;
+  nsresult rv = GetFeatureTasks(aChannel, features,
+                                nsIUrlClassifierFeature::blacklist, tasks);
+  if (NS_WARN_IF(NS_FAILED(rv)) || tasks.IsEmpty()) {
     return rv;
   }
 
-  nsTArray<nsCOMPtr<nsIUrlClassifierFeature>> features;
-  UrlClassifierFeatureFactory::GetFeaturesFromChannel(aChannel, features);
-  if (features.IsEmpty()) {
-    UC_LOG(("AsyncUrlChannelClassifier: Feature list is empty for channel %p",
-            aChannel));
-    return NS_ERROR_FAILURE;
-  }
+  MOZ_ASSERT(!tasks.IsEmpty());
 
   nsCOMPtr<nsIURIClassifier> uriClassifier =
       do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  nsCOMPtr<nsIUrlClassifierFeatureCallback> callback =
-      new BlacklistClassifierCallback(aChannel, uri, std::move(aCallback));
+  RefPtr<BlacklistClassifierCallback> callback =
+      new BlacklistClassifierCallback(aChannel, std::move(aCallback));
 
-  if (UC_LOG_ENABLED()) {
-    nsCString spec = uri->GetSpecOrDefault();
-    spec.Truncate(std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
-    UC_LOG(("AsyncUrlChannelClassifier: Checking blacklist for uri=%s\n",
-            spec.get()));
+  uint32_t pendingCallbacks = 0;
+  for (FeatureTask& task : tasks) {
+    if (UC_LOG_ENABLED()) {
+      nsCString spec = task.mURI->GetSpecOrDefault();
+      spec.Truncate(
+          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
+      UC_LOG(("AsyncUrlChannelClassifier: Checking blacklist for uri=%s\n",
+              spec.get()));
+    }
+
+    rv = uriClassifier->AsyncClassifyLocalWithFeatures(
+        task.mURI, task.mFeatures, nsIUrlClassifierFeature::blacklist,
+        callback);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    ++pendingCallbacks;
   }
 
-  // xpcom parser creates array of interfaces using RefPtr<>.
-  nsTArray<RefPtr<nsIUrlClassifierFeature>> refPtrFeatures;
-  for (nsIUrlClassifierFeature* feature : features) {
-    refPtrFeatures.AppendElement(feature);
+  // All the AsyncClassifyLocalWithFeatures() calls return error. We do not
+  // expect callbacks.
+  if (pendingCallbacks == 0) {
+    return NS_ERROR_FAILURE;
   }
 
-  return uriClassifier->AsyncClassifyLocalWithFeatures(
-      uri, refPtrFeatures, nsIUrlClassifierFeature::blacklist, callback);
+  callback->SetTaskCount(pendingCallbacks);
+  return NS_OK;
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierCommon.cpp
+++ b/netwerk/url-classifier/UrlClassifierCommon.cpp
@@ -239,10 +239,71 @@ UrlClassifierCommon::ShouldEnableTrackin
 
   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, category, doc,
                                   nsContentUtils::eNECKO_PROPERTIES, message,
                                   params, ArrayLength(params));
 
   return NS_OK;
 }
 
+/* static */ nsresult UrlClassifierCommon::CreatePairwiseWhiteListURI(
+    nsIChannel* aChannel, nsIURI** aURI) {
+  MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(aURI);
+
+  nsresult rv;
+  nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!chan) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIURI> topWinURI;
+  rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!topWinURI) {
+    if (UC_LOG_ENABLED()) {
+      nsresult rv;
+      nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(aChannel, &rv);
+      nsCOMPtr<nsIURI> uri;
+      rv = httpChan->GetURI(getter_AddRefs(uri));
+      nsAutoCString spec;
+      uri->GetAsciiSpec(spec);
+      spec.Truncate(
+          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
+      UC_LOG(("CreatePairwiseWhiteListURI: No window URI associated with %s",
+              spec.get()));
+    }
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIScriptSecurityManager> securityManager =
+      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIPrincipal> chanPrincipal;
+  rv = securityManager->GetChannelURIPrincipal(aChannel,
+                                               getter_AddRefs(chanPrincipal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Craft a whitelist URL like "toplevel.page/?resource=third.party.domain"
+  nsAutoCString pageHostname, resourceDomain;
+  rv = topWinURI->GetHost(pageHostname);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = chanPrincipal->GetBaseDomain(resourceDomain);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsAutoCString whitelistEntry = NS_LITERAL_CSTRING("http://") + pageHostname +
+                                 NS_LITERAL_CSTRING("/?resource=") +
+                                 resourceDomain;
+  UC_LOG(
+      ("CreatePairwiseWhiteListURI: Looking for %s in the whitelist "
+       "(channel=%p)",
+       whitelistEntry.get(), aChannel));
+
+  nsCOMPtr<nsIURI> whitelistURI;
+  rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  whitelistURI.forget(aURI);
+  return NS_OK;
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierCommon.h
+++ b/netwerk/url-classifier/UrlClassifierCommon.h
@@ -36,14 +36,19 @@ class UrlClassifierCommon final {
   static bool ShouldEnableTrackingProtectionOrAnnotation(
       nsIChannel* aChannel,
       AntiTrackingCommon::ContentBlockingAllowListPurpose aBlockingPurpose);
 
   static nsresult SetBlockedContent(nsIChannel* channel, nsresult aErrorCode,
                                     const nsACString& aList,
                                     const nsACString& aProvider,
                                     const nsACString& aFullHash);
+
+  // Use this function only when you are looking for a pairwise whitelist uri
+  // with the format: http://toplevel.page/?resource=channel.uri.domain
+  static nsresult CreatePairwiseWhiteListURI(nsIChannel* aChannel,
+                                             nsIURI** aURI);
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_UrlClassifierCommon_h
--- a/netwerk/url-classifier/UrlClassifierFeatureFactory.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureFactory.cpp
@@ -2,41 +2,44 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/net/UrlClassifierFeatureFactory.h"
 
 // List of Features
+#include "UrlClassifierFeatureFlash.h"
 #include "UrlClassifierFeatureLoginReputation.h"
 #include "UrlClassifierFeatureTrackingProtection.h"
 #include "UrlClassifierFeatureTrackingAnnotation.h"
 
 #include "nsAppRunner.h"
 
 namespace mozilla {
 namespace net {
 
 /* static */ void UrlClassifierFeatureFactory::Initialize() {
   // We want to expose Features only in the parent process.
   if (!XRE_IsParentProcess()) {
     return;
   }
 
+  UrlClassifierFeatureFlash::Initialize();
   UrlClassifierFeatureTrackingAnnotation::Initialize();
   UrlClassifierFeatureTrackingProtection::Initialize();
 }
 
 /* static */ void UrlClassifierFeatureFactory::Shutdown() {
   // We want to expose Features only in the parent process.
   if (!XRE_IsParentProcess()) {
     return;
   }
 
+  UrlClassifierFeatureFlash::Shutdown();
   UrlClassifierFeatureLoginReputation::MaybeShutdown();
   UrlClassifierFeatureTrackingAnnotation::Shutdown();
   UrlClassifierFeatureTrackingProtection::Shutdown();
 }
 
 /* static */ void UrlClassifierFeatureFactory::GetFeaturesFromChannel(
     nsIChannel* aChannel,
     nsTArray<nsCOMPtr<nsIUrlClassifierFeature>>& aFeatures) {
@@ -56,16 +59,21 @@ namespace net {
     aFeatures.AppendElement(feature);
   }
 
   // Tracking Annotation
   feature = UrlClassifierFeatureTrackingAnnotation::MaybeCreate(aChannel);
   if (feature) {
     aFeatures.AppendElement(feature);
   }
+
+  // Flash
+  nsTArray<nsCOMPtr<nsIUrlClassifierFeature>> flashFeatures;
+  UrlClassifierFeatureFlash::MaybeCreate(aChannel, flashFeatures);
+  aFeatures.AppendElements(flashFeatures);
 }
 
 /* static */
 nsIUrlClassifierFeature*
 UrlClassifierFeatureFactory::GetFeatureLoginReputation() {
   return UrlClassifierFeatureLoginReputation::MaybeGetOrCreate();
 }
 
new file mode 100644
--- /dev/null
+++ b/netwerk/url-classifier/UrlClassifierFeatureFlash.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "UrlClassifierFeatureFlash.h"
+#include "mozilla/net/HttpBaseChannel.h"
+#include "nsScriptSecurityManager.h"
+#include "nsQueryObject.h"
+
+namespace mozilla {
+namespace net {
+
+namespace {
+
+struct FlashFeatures {
+  const char* mName;
+  const char* mBlacklistPrefTables;
+  const char* mWhitelistPrefTables;
+  bool mSubdocumentOnly;
+  nsIHttpChannel::FlashPluginState mFlashPluginState;
+  RefPtr<UrlClassifierFeatureFlash> mFeature;
+};
+
+static FlashFeatures sFlashFeaturesMap[] = {
+    {"flash-deny", "urlclassifier.flashTable", "urlclassifier.flashExceptTable",
+     false, nsIHttpChannel::FlashPluginDenied},
+    {"flash-allow", "urlclassifier.flashAllowTable",
+     "urlclassifier.flashAllowExceptTable", false,
+     nsIHttpChannel::FlashPluginAllowed},
+    {"flash-deny-subdoc", "urlclassifier.flashSubDocTable",
+     "urlclassifier.flashSubDocExceptTable", true,
+     nsIHttpChannel::FlashPluginDeniedInSubdocuments},
+};
+
+}  // namespace
+
+UrlClassifierFeatureFlash::UrlClassifierFeatureFlash(uint32_t aId)
+    : UrlClassifierFeatureBase(
+          nsDependentCString(sFlashFeaturesMap[aId].mName),
+          nsDependentCString(sFlashFeaturesMap[aId].mBlacklistPrefTables),
+          nsDependentCString(sFlashFeaturesMap[aId].mWhitelistPrefTables),
+          EmptyCString(),  // aPrefBlacklistHosts
+          EmptyCString(),  // aPrefWhitelistHosts
+          EmptyCString(),  // aPrefBlacklistTableName
+          EmptyCString(),  // aPrefWhitelistTableName
+          EmptyCString())  // aPrefSkipHosts
+      ,
+      mFlashPluginState(sFlashFeaturesMap[aId].mFlashPluginState) {
+  static_assert(nsIHttpChannel::FlashPluginDeniedInSubdocuments ==
+                    nsIHttpChannel::FlashPluginLastValue,
+                "nsIHttpChannel::FlashPluginLastValue is out-of-sync!");
+}
+
+/* static */ void UrlClassifierFeatureFlash::Initialize() {
+  uint32_t numFeatures =
+      (sizeof(sFlashFeaturesMap) / sizeof(sFlashFeaturesMap[0]));
+  for (uint32_t i = 0; i < numFeatures; ++i) {
+    MOZ_ASSERT(!sFlashFeaturesMap[i].mFeature);
+    sFlashFeaturesMap[i].mFeature = new UrlClassifierFeatureFlash(i);
+    sFlashFeaturesMap[i].mFeature->InitializePreferences();
+  }
+}
+
+/* static */ void UrlClassifierFeatureFlash::Shutdown() {
+  uint32_t numFeatures =
+      (sizeof(sFlashFeaturesMap) / sizeof(sFlashFeaturesMap[0]));
+  for (uint32_t i = 0; i < numFeatures; ++i) {
+    MOZ_ASSERT(sFlashFeaturesMap[i].mFeature);
+    sFlashFeaturesMap[i].mFeature->ShutdownPreferences();
+    sFlashFeaturesMap[i].mFeature = nullptr;
+  }
+}
+
+/* static */ void UrlClassifierFeatureFlash::MaybeCreate(
+    nsIChannel* aChannel,
+    nsTArray<nsCOMPtr<nsIUrlClassifierFeature>>& aFeatures) {
+  // All disabled.
+  if (!StaticPrefs::plugins_flashBlock_enabled()) {
+    return;
+  }
+
+  // We use Flash feature just for document loading.
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  nsContentPolicyType contentPolicyType =
+      loadInfo ? loadInfo->GetExternalContentPolicyType()
+               : nsIContentPolicy::TYPE_INVALID;
+
+  if (contentPolicyType != nsIContentPolicy::TYPE_DOCUMENT &&
+      contentPolicyType != nsIContentPolicy::TYPE_SUBDOCUMENT) {
+    return;
+  }
+
+  // Only allow plugins for documents from an HTTP/HTTPS origin.
+  if (StaticPrefs::plugins_http_https_only()) {
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+    if (!httpChannel) {
+      return;
+    }
+  }
+
+  uint32_t numFeatures =
+      (sizeof(sFlashFeaturesMap) / sizeof(sFlashFeaturesMap[0]));
+  for (uint32_t i = 0; i < numFeatures; ++i) {
+    MOZ_ASSERT(sFlashFeaturesMap[i].mFeature);
+    if (!sFlashFeaturesMap[i].mSubdocumentOnly ||
+        contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
+      aFeatures.AppendElement(sFlashFeaturesMap[i].mFeature);
+    }
+  }
+}
+
+NS_IMETHODIMP
+UrlClassifierFeatureFlash::ProcessChannel(nsIChannel* aChannel,
+                                          const nsACString& aList,
+                                          bool* aShouldContinue) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aShouldContinue);
+
+  // This is not a blocking feature.
+  *aShouldContinue = true;
+
+  UC_LOG(("UrlClassifierFeatureFlash::ProcessChannel, annotating channel[%p]",
+          aChannel));
+
+  nsCOMPtr<nsIParentChannel> parentChannel;
+  NS_QueryNotificationCallbacks(aChannel, parentChannel);
+  if (parentChannel) {
+    // This channel is a parent-process proxy for a child process
+    // request. We should notify the child process as well.
+    parentChannel->NotifyFlashPluginStateChanged(mFlashPluginState);
+  }
+
+  RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
+  if (httpChannel) {
+    httpChannel->SetFlashPluginState(mFlashPluginState);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierFeatureFlash::GetURIByListType(
+    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
+    nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURI);
+
+  // Here we return the channel's URI always.
+  return aChannel->GetURI(aURI);
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/url-classifier/UrlClassifierFeatureFlash.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_UrlClassifierFeatureFlash_h
+#define mozilla_UrlClassifierFeatureFlash_h
+
+#include "UrlClassifierFeatureBase.h"
+
+namespace mozilla {
+namespace net {
+
+class UrlClassifierFeatureFlash final : public UrlClassifierFeatureBase {
+ public:
+  static void Initialize();
+  static void Shutdown();
+
+  static void MaybeCreate(
+      nsIChannel* aChannel,
+      nsTArray<nsCOMPtr<nsIUrlClassifierFeature>>& aFeatures);
+
+  NS_IMETHOD
+  ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
+                 bool* aShouldContinue) override;
+
+  NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
+                              nsIUrlClassifierFeature::listType aListType,
+                              nsIURI** aURI) override;
+
+ private:
+  explicit UrlClassifierFeatureFlash(uint32_t aId);
+
+  nsIHttpChannel::FlashPluginState mFlashPluginState;
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_UrlClassifierFeatureFlash_h
--- a/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.cpp
@@ -88,10 +88,23 @@ UrlClassifierFeatureLoginReputation::Has
     nsACString& aPrefTableName, bool* aResult) {
   MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist,
              "UrlClassifierFeatureLoginReputation is meant to be used just to "
              "whitelist URLs");
   return UrlClassifierFeatureBase::HasHostInPreferences(
       aHost, aListType, aPrefTableName, aResult);
 }
 
+NS_IMETHODIMP
+UrlClassifierFeatureLoginReputation::GetURIByListType(
+    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
+    nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURI);
+  MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist,
+             "UrlClassifierFeatureLoginReputation is meant to be used just to "
+             "whitelist URLs");
+
+  return aChannel->GetURI(aURI);
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureLoginReputation.h
@@ -32,16 +32,20 @@ class UrlClassifierFeatureLoginReputatio
   NS_IMETHOD
   HasHostInPreferences(const nsACString& aHost,
                        nsIUrlClassifierFeature::listType aListType,
                        nsACString& aPrefTableName, bool* aResult) override;
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
                             bool* aShouldContinue) override;
 
+  NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
+                              nsIUrlClassifierFeature::listType aListType,
+                              nsIURI** aURI) override;
+
  private:
   UrlClassifierFeatureLoginReputation();
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_UrlClassifierFeatureLoginReputation_h
--- a/netwerk/url-classifier/UrlClassifierFeatureResult.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureResult.cpp
@@ -5,22 +5,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/net/UrlClassifierFeatureResult.h"
 
 namespace mozilla {
 namespace net {
 
 UrlClassifierFeatureResult::UrlClassifierFeatureResult(
-    nsIUrlClassifierFeature* aFeature, const nsACString& aList)
-    : mFeature(aFeature), mList(aList) {}
+    nsIURI* aURI, nsIUrlClassifierFeature* aFeature, const nsACString& aList)
+    : mURI(aURI), mFeature(aFeature), mList(aList) {}
 
 UrlClassifierFeatureResult::~UrlClassifierFeatureResult() = default;
 
 NS_IMETHODIMP
+UrlClassifierFeatureResult::GetUri(nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aURI);
+  nsCOMPtr<nsIURI> uri = mURI;
+  uri.forget(aURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 UrlClassifierFeatureResult::GetFeature(nsIUrlClassifierFeature** aFeature) {
   NS_ENSURE_ARG_POINTER(aFeature);
   nsCOMPtr<nsIUrlClassifierFeature> feature = mFeature;
   feature.forget(aFeature);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/url-classifier/UrlClassifierFeatureResult.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureResult.h
@@ -5,36 +5,41 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_net_UrlClassifierFeatureResult_h
 #define mozilla_net_UrlClassifierFeatureResult_h
 
 #include "nsIUrlClassifierFeature.h"
 #include "nsString.h"
 
+class nsIURI;
+
 namespace mozilla {
 namespace net {
 
 class UrlClassifierFeatureResult final : public nsIUrlClassifierFeatureResult {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIURLCLASSIFIERFEATURERESULT
 
-  UrlClassifierFeatureResult(nsIUrlClassifierFeature* aFeature,
+  UrlClassifierFeatureResult(nsIURI* aURI, nsIUrlClassifierFeature* aFeature,
                              const nsACString& aList);
 
+  nsIURI* URI() const { return mURI; }
+
   nsIUrlClassifierFeature* Feature() const { return mFeature; }
 
   // Comma separated list of tables.
   const nsCString& List() const { return mList; }
 
  protected:
   ~UrlClassifierFeatureResult();
 
  private:
+  nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIUrlClassifierFeature> mFeature;
   const nsCString mList;
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_UrlClassifierFeatureResult_h
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.cpp
@@ -196,10 +196,25 @@ UrlClassifierFeatureTrackingAnnotation::
     if (StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
       LowerPriorityHelper(aChannel);
     }
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+UrlClassifierFeatureTrackingAnnotation::GetURIByListType(
+    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
+    nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURI);
+
+  if (aListType == nsIUrlClassifierFeature::blacklist) {
+    return aChannel->GetURI(aURI);
+  }
+
+  MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+  return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingAnnotation.h
@@ -22,16 +22,20 @@ class UrlClassifierFeatureTrackingAnnota
   static void Shutdown();
 
   static already_AddRefed<UrlClassifierFeatureTrackingAnnotation> MaybeCreate(
       nsIChannel* aChannel);
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
                             bool* aShouldContinue) override;
 
+  NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
+                              nsIUrlClassifierFeature::listType aListType,
+                              nsIURI** aURI) override;
+
  private:
   UrlClassifierFeatureTrackingAnnotation();
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_UrlClassifierFeatureTrackingAnnotation_h
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.cpp
@@ -125,10 +125,25 @@ UrlClassifierFeatureTrackingProtection::
     Unused << httpChannel->CancelForTrackingProtection();
   } else {
     Unused << aChannel->Cancel(NS_ERROR_TRACKING_URI);
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+UrlClassifierFeatureTrackingProtection::GetURIByListType(
+    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
+    nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURI);
+
+  if (aListType == nsIUrlClassifierFeature::blacklist) {
+    return aChannel->GetURI(aURI);
+  }
+
+  MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+  return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.h
+++ b/netwerk/url-classifier/UrlClassifierFeatureTrackingProtection.h
@@ -22,16 +22,20 @@ class UrlClassifierFeatureTrackingProtec
   static void Shutdown();
 
   static already_AddRefed<UrlClassifierFeatureTrackingProtection> MaybeCreate(
       nsIChannel* aChannel);
 
   NS_IMETHOD ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
                             bool* aShouldContinue) override;
 
+  NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
+                              nsIUrlClassifierFeature::listType aListType,
+                              nsIURI** aURI) override;
+
  private:
   UrlClassifierFeatureTrackingProtection();
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_UrlClassifierFeatureTrackingProtection_h
--- a/netwerk/url-classifier/moz.build
+++ b/netwerk/url-classifier/moz.build
@@ -18,16 +18,17 @@ DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = Tru
 DEFINES['GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER'] = True
 
 UNIFIED_SOURCES += [
     'AsyncUrlChannelClassifier.cpp',
     'nsChannelClassifier.cpp',
     'UrlClassifierCommon.cpp',
     'UrlClassifierFeatureBase.cpp',
     'UrlClassifierFeatureFactory.cpp',
+    'UrlClassifierFeatureFlash.cpp',
     'UrlClassifierFeatureLoginReputation.cpp',
     'UrlClassifierFeatureResult.cpp',
     'UrlClassifierFeatureTrackingAnnotation.cpp',
     'UrlClassifierFeatureTrackingProtection.cpp',
 ]
 
 EXPORTS.mozilla.net += [
     'AsyncUrlChannelClassifier.h',
--- a/netwerk/url-classifier/nsIURIClassifier.idl
+++ b/netwerk/url-classifier/nsIURIClassifier.idl
@@ -77,23 +77,16 @@ interface nsIURIClassifier : nsISupports
    *         callback will be called.
    */
   boolean classify(in nsIPrincipal aPrincipal,
                    in nsIEventTarget aEventTarget,
                    in boolean aTrackingProtectionEnabled,
                    in nsIURIClassifierCallback aCallback);
 
   /**
-   * Synchronously classify a URI with a comma-separated string
-   * containing the given tables. This does not make network requests.
-   * The result is an array of table names that match.
-   */
-  [noscript] StringArrayRef classifyLocalWithTables(in nsIURI aURI, in ACString aTables);
-
-  /**
    * Asynchronously classify a URI with a comma-separated string
    * containing the given tables. This does not make network requests.
    * The callback does NOT totally follow nsIURIClassifierCallback's
    * semantics described above. Only |aList| will be meaningful, which
    * is a comma separated list of table names. (same as what classifyLocal
    * returns.)
    */
   void asyncClassifyLocalWithTables(in nsIURI aURI,
@@ -107,22 +100,16 @@ interface nsIURIClassifier : nsISupports
    * network requests.
    */
   void asyncClassifyLocalWithFeatures(in nsIURI aURI,
                                       in Array<nsIUrlClassifierFeature> aFeatures,
                                       in nsIUrlClassifierFeature_listType aListType,
                                       in nsIUrlClassifierFeatureCallback aCallback);
 
   /**
-   * Same as above, but returns a comma separated list of table names.
-   * This is an internal interface used only for testing purposes.
-   */
-  ACString classifyLocal(in nsIURI aURI, in ACString aTables);
-
-  /**
    * Report to the provider that a Safe Browsing warning was shown.
    *
    * @param aChannel
    *        Channel for which the URL matched something on the threat list.
    * @param aProvider
    *        Provider to notify.
    * @param aList
    *        List where the full hash was found.
--- a/netwerk/url-classifier/nsIUrlClassifierFeature.idl
+++ b/netwerk/url-classifier/nsIUrlClassifierFeature.idl
@@ -6,16 +6,17 @@
 
 %{C++
 #include "nsStringFwd.h"
 #include "nsTArrayForwardDeclare.h"
 %}
 [ref] native StringArrayRef(nsTArray<nsCString>);
 
 interface nsIChannel;
+interface nsIURI;
 
 /**
  * A single URLClassifier feature.
  */
 [builtinclass, scriptable, uuid(a6c9b24e-b4f1-426e-af58-2c976c3943a8)]
 interface nsIUrlClassifierFeature : nsISupports
 {
   cenum listType: 8 {
@@ -57,25 +58,35 @@ interface nsIUrlClassifierFeature : nsIS
    * 'something' on the channel. For instance, a tracking-annotation feature
    * would mark the channel as tracker, a tracking-protection feature would
    * cancel the channel.
    * Returns if we should process other feature results or not. For instance,
    * tracking-protection cancel the channel, and after that we should stop
    * processing other features.
    */
   [noscript] boolean processChannel(in nsIChannel aChannel, in ACString aList);
+
+  /**
+   * Features can work with different URLs from a channel (channel url, or
+   * top-level, or something else). This method returns what we need to use for
+   * the current list.
+   */
+  [noscript] nsIURI getURIByListType(in nsIChannel channel,
+                                     in nsIUrlClassifierFeature_listType listType);
 };
 
 /**
  * The result of the classifier operation is this interface.
  * See asyncClassifyLocalWithFeatures() in nsIURIClassifier.idl.
  */
 [builtinclass, scriptable, uuid(ccb88140-5d66-4873-9815-a1b98d6cdc92)]
 interface nsIUrlClassifierFeatureResult : nsISupports
 {
+  readonly attribute nsIURI uri;
+
   readonly attribute nsIUrlClassifierFeature feature;
 
   // Comma separate tables or preferences.
   readonly attribute ACString list;
 };
 
 /**
  * Callback function for nsIURIClassifier lookups.
--- a/taskcluster/ci/test/misc.yml
+++ b/taskcluster/ci/test/misc.yml
@@ -4,32 +4,32 @@ geckoview-junit:
     treeherder-symbol: gv-junit
     instance-size: xlarge
     loopback-video: true
     e10s: true
     target: geckoview-androidTest.apk
     max-run-time: 3600
     tier:
         by-test-platform:
-            android-em-7.0-x86/opt: 3
+            android-em-7.0-x86/.*: 3
             default: default
     chunks:
         by-test-platform:
             android-em-4.3-arm7-api-16-ccov/debug: 6
             android-em-4.3-arm7-api-16/debug: 6
             android-em-4.3-arm7-api-16/opt: 2
             default: 1
     mozharness:
         script: android_emulator_unittest.py
         config:
             by-test-platform:
-                android-em-7.0-x86/opt:
+                android-em-7.0-x86/.*:
                     - android/android_common.py
                     - android/androidx86_7_0.py
-                android-em.*:
+                android-em-4.*:
                     - android/android_common.py
                     - android/androidarm_4_3_junit.py
         extra-options:
             - --test-suite=geckoview-junit
 
 robocop:
     description: "Robocop run"
     suite: robocop
--- a/taskcluster/ci/test/mochitest.yml
+++ b/taskcluster/ci/test/mochitest.yml
@@ -1,29 +1,29 @@
 job-defaults:
     target:
         by-test-platform:
-            android-em-7.0-x86/opt: geckoview-androidTest.apk
+            android-em-7.*: geckoview-androidTest.apk
             default: null
     serviceworker-e10s:
         by-test-platform:
             linux64/debug: both
             default: false
     mozharness:
         script:
             by-test-platform:
                 android-em.*: android_emulator_unittest.py
                 android-hw.*: android_hardware_unittest.py
                 default: desktop_unittest.py
         config:
             by-test-platform:
-                android-em-7.0-x86/opt:
+                android-em-7.*:
                     - android/android_common.py
                     - android/androidx86_7_0.py
-                android-em.*:
+                android-em-4.*:
                     - android/android_common.py
                     - android/androidarm_4_3.py
                 android-hw.*:
                     - android/android_common.py
                     - android/android_hw.py
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
@@ -45,18 +45,18 @@ mochitest:
             default: default
     virtualization:
         by-test-platform:
             windows10-64-qr/.*: virtual-with-gpu
             default: virtual
     chunks:
         by-test-platform:
             android-em-4.3-arm7-api-16/debug: 60
-            android-em-7.0-x86/opt: 4
-            android-em.*: 24
+            android-em-4.*: 24
+            android-em-7.*: 4
             linux.*/debug: 16
             linux64-asan/opt: 10
             linux64-.*cov/opt: 10
             windows10-64-ccov/debug: 10
             macosx64-ccov/debug: 10
             default: 5
     e10s:
         by-test-platform:
@@ -272,17 +272,17 @@ mochitest-media:
             windows10-64-qr/.*: virtual-with-gpu
             default: virtual
     instance-size:
         by-test-platform:
             android-em.*: xlarge
             default: large
     chunks:
         by-test-platform:
-            android-em-7.0-x86/opt: 1
+            android-em-7.*: 1
             macosx64.*: 1
             windows10-64.*: 1
             default: 3
     mozharness:
         mochitest-flavor: plain
         chunked:
             by-test-platform:
                 android.*: false
--- a/taskcluster/ci/test/reftest.yml
+++ b/taskcluster/ci/test/reftest.yml
@@ -1,28 +1,28 @@
 job-defaults:
     target:
         by-test-platform:
-            android-em-7.0-x86/opt: geckoview-androidTest.apk
+            android-em-7.*: geckoview-androidTest.apk
             default: null
     serviceworker-e10s:
         by-test-platform:
             linux64/debug: both
             default: false
     mozharness:
         script:
             by-test-platform:
                 android-em.*: android_emulator_unittest.py
                 default: desktop_unittest.py
         config:
             by-test-platform:
-                android-em-7.0-x86/opt:
+                android-em-7.*:
                     - android/android_common.py
                     - android/androidx86_7_0.py
-                android-em.*:
+                android-em-4.*:
                     - android/android_common.py
                     - android/androidarm_4_3.py
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
                 macosx.*:
                     - unittests/mac_unittest.py
                 windows.*:
@@ -39,18 +39,18 @@ crashtest:
             default: default
     virtualization:
         by-test-platform:
             windows10-64-qr/.*: virtual-with-gpu
             default: virtual
     chunks:
         by-test-platform:
             android-em-4.3-arm7-api-16/debug: 10
-            android-em-7.0-x86/opt: 1
-            android-em.*: 4
+            android-em-4.3-arm7-api-16/opt: 4
+            android-em-7.*: 1
             default: 1
     e10s:
         by-test-platform:
             linux32/debug: both
             default: true
     max-run-time:
         by-test-platform:
             android-em.*: 7200
@@ -65,16 +65,17 @@ jsreftest:
     instance-size:
         by-test-platform:
             android-em.*: xlarge
             default: default
     chunks:
         by-test-platform:
             android-em-4.3-arm7-api-16/debug: 100
             android-em-7.0-x86/opt: 4
+            android-em-7.0-x86/debug: 8
             android-em.*: 40
             windows.*: 2
             windows10-64-ccov/debug: 5
             linux64-ccov/.*: 5
             linux64-qr/.*: 4
             linux32/debug: 5
             macosx64-ccov/debug: 5
             default: 3
@@ -93,18 +94,18 @@ reftest:
     instance-size:
         by-test-platform:
             android-em.*: xlarge
             default: default
     virtualization: virtual-with-gpu
     chunks:
         by-test-platform:
             android-em-4.3-arm7-api-16/debug: 56
-            android-em-7.0-x86/opt: 5
-            android-em.*: 28
+            android-em-4.*: 28
+            android-em-7.*: 5
             macosx64.*/opt: 2
             macosx64.*/debug: 3
             macosx64-ccov/debug: 6
             windows.*/opt: 2
             windows.*/debug: 4
             windows10-64-ccov/debug: 6
             default: 8
     e10s:
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -596,17 +596,18 @@ def set_treeherder_machine_platform(conf
         # platform based on regular macOS builds, such as for QR.
         # Since it's unclear if the regular macOS builds can be removed from
         # the table, workaround the issue for QR.
         if '-qr' in test['test-platform']:
             test['treeherder-machine-platform'] = test['test-platform']
         elif 'android-hw' in test['test-platform']:
             test['treeherder-machine-platform'] = test['test-platform']
         elif 'android-em-7.0-x86' in test['test-platform']:
-            test['treeherder-machine-platform'] = 'android-em-7-0-x86/opt'
+            opt = test['test-platform'].split('/')[1]
+            test['treeherder-machine-platform'] = 'android-em-7-0-x86/'+opt
         else:
             test['treeherder-machine-platform'] = translation.get(
                 test['build-platform'], test['test-platform'])
         yield test
 
 
 @transforms.add
 def set_tier(config, tests):
--- a/testing/mochitest/runjunit.py
+++ b/testing/mochitest/runjunit.py
@@ -62,18 +62,17 @@ class JUnitTestRunner(MochitestDesktop):
 
         self.server_init()
 
         self.cleanup()
         self.device.clear_logcat()
         self.build_profile()
         self.startServers(
             self.options,
-            debuggerInfo=None,
-            ignoreSSLTunnelExts=True)
+            debuggerInfo=None)
         self.log.debug("Servers started")
 
     def server_init(self):
         """
            Additional initialization required to satisfy MochitestDesktop.startServers
         """
         self._locations = None
         self.server = None
--- a/testing/mochitest/runrobocop.py
+++ b/testing/mochitest/runrobocop.py
@@ -118,21 +118,19 @@ class RobocopTestRunner(MochitestDesktop
             self.device.install_app(self.options.robocopApk, replace=True)
             self.log.debug("Robocop APK %s installed" %
                            self.options.robocopApk)
         # Display remote diagnostics; if running in mach, keep output terse.
         if self.options.log_mach is None:
             self.printDeviceInfo()
         self.setupLocalPaths()
         self.buildProfile()
-        # ignoreSSLTunnelExts is a workaround for bug 1109310
         self.startServers(
             self.options,
-            debuggerInfo=None,
-            ignoreSSLTunnelExts=True)
+            debuggerInfo=None)
         self.log.debug("Servers started")
 
     def cleanup(self):
         """
            Cleanup at end of job run.
         """
         self.log.debug("Cleaning up...")
         self.stopServers()
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -563,27 +563,26 @@ class WebSocketServer(object):
         self._log.info("runtests.py | Websocket server pid: %d" % pid)
 
     def stop(self):
         self._process.kill()
 
 
 class SSLTunnel:
 
-    def __init__(self, options, logger, ignoreSSLTunnelExts=False):
+    def __init__(self, options, logger):
         self.log = logger
         self.process = None
         self.utilityPath = options.utilityPath
         self.xrePath = options.xrePath
         self.certPath = options.certPath
         self.sslPort = options.sslPort
         self.httpPort = options.httpPort
         self.webServer = options.webServer
         self.webSocketPort = options.webSocketPort
-        self.useSSLTunnelExts = not ignoreSSLTunnelExts
 
         self.customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)")
         self.clientAuthRE = re.compile("^clientauth=(?P<clientauth>[a-z]+)")
         self.redirRE = re.compile("^redir=(?P<redirhost>[0-9a-zA-Z_ .]+)")
 
     def writeLocation(self, config, loc):
         for option in loc.options:
             match = self.customCertRE.match(option)
@@ -599,17 +598,17 @@ class SSLTunnel:
                              (loc.host, loc.port, self.sslPort, clientauth))
 
             match = self.redirRE.match(option)
             if match:
                 redirhost = match.group("redirhost")
                 config.write("redirhost:%s:%s:%s:%s\n" %
                              (loc.host, loc.port, self.sslPort, redirhost))
 
-            if self.useSSLTunnelExts and option in (
+            if option in (
                     'tls1',
                     'ssl3',
                     'rc4',
                     'failHandshake'):
                 config.write(
                     "%s:%s:%s:%s\n" %
                     (option, loc.host, loc.port, self.sslPort))
 
@@ -1140,17 +1139,17 @@ class MochitestDesktop(object):
                 sock.close()
                 break
             except Exception:
                 time.sleep(0.1)
         else:
             self.log.error("runtests.py | Timed out while waiting for "
                            "websocket/process bridge startup.")
 
-    def startServers(self, options, debuggerInfo, ignoreSSLTunnelExts=False):
+    def startServers(self, options, debuggerInfo):
         # start servers and set ports
         # TODO: pass these values, don't set on `self`
         self.webServer = options.webServer
         self.httpPort = options.httpPort
         self.sslPort = options.sslPort
         self.webSocketPort = options.webSocketPort
 
         # httpd-path is specified by standard makefile targets and may be specified
@@ -1164,18 +1163,17 @@ class MochitestDesktop(object):
         self.startWebSocketServer(options, debuggerInfo)
 
         if options.subsuite in ["media"]:
             self.startWebsocketProcessBridge(options)
 
         # start SSL pipe
         self.sslTunnel = SSLTunnel(
             options,
-            logger=self.log,
-            ignoreSSLTunnelExts=ignoreSSLTunnelExts)
+            logger=self.log)
         self.sslTunnel.buildConfig(self.locations)
         self.sslTunnel.start()
 
         # If we're lucky, the server has fully started by now, and all paths are
         # ready, etc.  However, xpcshell cold start times suck, at least for debug
         # builds.  We'll try to connect to the server for awhile, and if we fail,
         # we'll try to kill the server and exit with an error.
         if self.server is not None:
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -211,22 +211,20 @@ class MochiRemote(MochitestDesktop):
             options.utilityPath = remoteUtilityPath
             options.profilePath = remoteProfilePath
 
         return fixup
 
     def startServers(self, options, debuggerInfo):
         """ Create the servers on the host and start them up """
         restoreRemotePaths = self.switchToLocalPaths(options)
-        # ignoreSSLTunnelExts is a workaround for bug 1109310
         MochitestDesktop.startServers(
             self,
             options,
-            debuggerInfo,
-            ignoreSSLTunnelExts=True)
+            debuggerInfo)
         restoreRemotePaths()
 
     def buildProfile(self, options):
         restoreRemotePaths = self.switchToLocalPaths(options)
         if options.testingModulesDir:
             try:
                 self.device.push(options.testingModulesDir, self.remoteModulesDir)
                 self.device.chmod(self.remoteModulesDir, recursive=True, root=True)
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cast/.cargo-checksum.json
@@ -0,0 +1,1 @@
+{"files":{"CHANGELOG.md":"b24b688b552ea12f4ef03b2c76e17ecfb8c05104d4f494c47b698fd7d94d3e3a","Cargo.toml":"3908d7c2421a4071d3fdd4a7c7a44c1c8779cf9d229cec307fd1a2b96398aa60","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"005b3f9b4687364514bbaea2a25e96d54ce6a59277d157386e8259cbcae8e095","README.md":"006ae145ee89fa14a9d755b206ec2011a3687408e9d5fe3943f2448767b01d62","ci/install.sh":"f3965bda34345579d0799ca2e581efd7ae51f3191aa9203b018f4ade8b986b15","ci/script.sh":"0f8329c7345731c12f35a392e8deeb2e265b75f107c5aca6eed584096896737f","src/lib.rs":"52418274bc4ec5c59dba1885de7324157e677739a2c9624b5d462d5d4c77b5e3","src/test.rs":"58ea38d755d5eae72b1df29fc602483fef888a111bb2e9c3a219e2b2a1ed222f"},"package":"926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427"}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cast/CHANGELOG.md
@@ -0,0 +1,42 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/)
+and this project adheres to [Semantic Versioning](http://semver.org/).
+
+## [Unreleased]
+
+## [v0.2.2] - 2017-05-07
+
+### Fixed
+
+- UB in the checked cast from `f32` to `u128`.
+
+## [v0.2.1] - 2017-05-06
+
+### Added
+
+- Support for 128-bit integers, behind the `x128` Cargo feature (nightly
+  needed).
+
+## [v0.2.0] - 2017-02-08
+
+### Added
+
+- Now `cast::Error` implements the `std::error::Error` trait among other traits
+  like `Display`, `Clone`, etc.
+
+### Changed
+
+- [breaking-change] This crate now depends on the `std` crate by default but you
+  can make it `no_std` by opting out of the `std` Cargo feature.
+
+## v0.1.0 - 2016-02-07
+
+Initial release
+
+[Unreleased]: https://github.com/japaric/cast.rs/compare/v0.2.2...HEAD
+[v0.2.2]: https://github.com/japaric/cast.rs/compare/v0.2.1...v0.2.2
+[v0.2.1]: https://github.com/japaric/cast.rs/compare/v0.2.0...v0.2.1
+[v0.2.0]: https://github.com/japaric/cast.rs/compare/v0.1.0...v0.2.0
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cast/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+authors = ["Jorge Aparicio <jorge@japaric.io>"]
+description = "Ergonomic, checked cast functions for primitive types"
+documentation = "https://docs.rs/cast"
+keywords = ["checked", "cast", "primitive", "integer", "float"]
+license = "MIT OR Apache-2.0"
+name = "cast"
+repository = "https://github.com/japaric/cast.rs"
+version = "0.2.2"
+
+[features]
+# Assume we should use `std` unless asked to do otherwise.
+default = ["std"]
+# Enable this to get a std::error::Error impl for convenient use with other
+# libraries.
+std = []
+# Enable this for i128/u128 support
+x128 = []
+
+[dev-dependencies]
+quickcheck = "0.4.1"
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cast/LICENSE-APACHE
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cast/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2014-2017 Jorge Aparicio
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cast/README.md
@@ -0,0 +1,44 @@
+[![crates.io](https://img.shields.io/crates/d/cast.svg)](https://crates.io/crates/cast)
+[![crates.io](https://img.shields.io/crates/v/cast.svg)](https://crates.io/crates/cast)
+
+# `cast`
+
+> Ergonomic, checked cast functions for primitive types
+
+``` rust
+extern crate cast;
+
+// `u8` and `u16` are checked cast functions, use them to cast from any numeric
+// primitive to `u8`/`u16` respectively
+use cast::{u8, u16, Error};
+
+// Infallible operations, like integer promotion, are equivalent to a normal
+// cast with `as`
+assert_eq!(u16(0u8), 0u16);
+
+// Everything else will return a `Result` depending on the success of the
+// operation
+assert_eq!(u8(0u16), Ok(0u8));
+assert_eq!(u8(256u16), Err(Error::Overflow));
+assert_eq!(u8(-1i8), Err(Error::Underflow));
+assert_eq!(u8(1. / 0.), Err(Error::Infinite));
+assert_eq!(u8(0. / 0.), Err(Error::NaN));
+```
+
+## [API docs](https://docs.rs/cast)
+
+## License
+
+Licensed under either of
+
+- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
+  http://www.apache.org/licenses/LICENSE-2.0)
+- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cast/ci/install.sh
@@ -0,0 +1,21 @@
+set -ex
+
+main() {
+    curl https://sh.rustup.rs -sSf | \
+        sh -s -- -y --default-toolchain $TRAVIS_RUST_VERSION
+
+    local latest=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \
+                       | cut -d/ -f3 \
+                       | grep -E '^v[0-9.]+$' \
+                       | sort --version-sort \
+                       | tail -n1)
+
+    curl -LSfs https://japaric.github.io/trust/install.sh | \
+        sh -s -- \
+           --force \
+           --git japaric/cross \
+           --tag $latest \
+           --target x86_64-unknown-linux-gnu
+}
+
+main
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cast/ci/script.sh
@@ -0,0 +1,14 @@
+set -ex
+
+main() {
+    cross test --target $TARGET
+    cross test --target $TARGET --release
+
+    [ "$TRAVIS_RUST_VERSION" -eq "nightly" ] && cross test --feature x128 --target $TARGET
+    [ "$TRAVIS_RUST_VERSION" -eq "nightly" ] && cross test --feature x128 --target $TARGET --release
+
+    cross test --no-default-features --target $TARGET
+    cross test --no-default-features --target $TARGET --release
+}
+
+main
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cast/src/lib.rs
@@ -0,0 +1,523 @@
+//! Ergonomic, checked cast functions for primitive types
+//!
+//! This crate provides one checked cast function for each numeric primitive.
+//! Use these functions to perform a cast from any other numeric primitive:
+//!
+//! ```
+//! extern crate cast;
+//!
+//! use cast::{u8, u16, Error};
+//!
+//! # fn main() {
+//! // Infallible operations, like integer promotion, are equivalent to a normal
+//! // cast with `as`
+//! assert_eq!(u16(0u8), 0u16);
+//!
+//! // Everything else will return a `Result` depending on the success of the
+//! // operation
+//! assert_eq!(u8(0u16), Ok(0u8));
+//! assert_eq!(u8(256u16), Err(Error::Overflow));
+//! assert_eq!(u8(-1i8), Err(Error::Underflow));
+//! assert_eq!(u8(1. / 0.), Err(Error::Infinite));
+//! assert_eq!(u8(0. / 0.), Err(Error::NaN));
+//! # }
+//! ```
+//!
+//! There are no namespace problems between these functions, the "primitive
+//! modules" in `core`/`std` and the built-in primitive types, so all them can
+//! be in the same scope:
+//!
+//! ```
+//! extern crate cast;
+//!
+//! use std::u8;
+//! use cast::{u8, u16};
+//!
+//! # fn main() {
+//! // `u8` as a type
+//! let x: u8 = 0;
+//! // `u8` as a module
+//! let y = u16(u8::MAX);
+//! // `u8` as a function
+//! let z = u8(y).unwrap();
+//! # }
+//! ```
+//!
+//! The checked cast functionality is also usable with type aliases via the
+//! `cast` static method:
+//!
+//! ```
+//! extern crate cast;
+//!
+//! use std::os::raw::c_ulonglong;
+//! // NOTE avoid shadowing `std::convert::From` - cf. rust-lang/rfcs#1311
+//! use cast::From as _0;
+//!
+//! # fn main() {
+//! assert_eq!(c_ulonglong::cast(0u8), 0u64);
+//! # }
+//! ```
+//!
+//! This crate also provides a `From` trait that can be used, for example,
+//! to create a generic function that accepts any type that can be infallibly
+//! casted to `u32`.
+//!
+//! ```
+//! extern crate cast;
+//!
+//! fn to_u32<T>(x: T) -> u32
+//!     // reads as: "where u32 can be casted from T with output u32"
+//!     where u32: cast::From<T, Output=u32>,
+//! {
+//!     cast::u32(x)
+//! }
+//!
+//! # fn main() {
+//! assert_eq!(to_u32(0u8), 0u32);
+//! assert_eq!(to_u32(1u16), 1u32);
+//! assert_eq!(to_u32(2u32), 2u32);
+//!
+//! // to_u32(-1i32);  // Compile error
+//! # }
+//! ```
+//!
+//! ## Building without `std`
+//!
+//! This crate can be used without Rust's `std` crate by declaring it as
+//! follows in your `Cargo.toml`:
+//!
+//! ``` toml
+//! cast = { version = "*", default-features = false }
+//! ```
+
+#![deny(missing_docs)]
+#![deny(warnings)]
+#![allow(const_err)]
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#![cfg_attr(feature = "x128", feature(i128_type, i128))]
+
+#[cfg(feature = "std")]
+extern crate core;
+
+#[cfg(test)]
+#[macro_use]
+extern crate quickcheck;
+
+use core::fmt;
+#[cfg(feature="std")]
+use std::error;
+
+#[cfg(test)]
+mod test;
+
+/// Cast errors
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum Error {
+    /// Infinite value casted to a type that can only represent finite values
+    Infinite,
+    /// NaN value casted to a type that can't represent a NaN value
+    NaN,
+    /// Source value is greater than the maximum value that the destination type
+    /// can hold
+    Overflow,
+    /// Source value is smaller than the minimum value that the destination type
+    /// can hold
+    Underflow,
+}
+
+impl Error {
+    /// A private helper function that implements `description`, because
+    /// `description` is only available when we have `std` enabled.
+    fn description_helper(&self) -> &str {
+        match *self {
+            Error::Infinite => "Cannot store infinite value in finite type",
+            Error::NaN => "Cannot store NaN in type which does not support it",
+            Error::Overflow => "Overflow during numeric conversion",
+            Error::Underflow => "Underflow during numeric conversion",
+        }
+    }
+}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", self.description_helper())
+    }
+}
+
+#[cfg(feature="std")]
+impl error::Error for Error {
+    fn description(&self) -> &str {
+        self.description_helper()
+    }
+}
+
+/// The "cast from" operation
+pub trait From<Src> {
+    /// The result of the cast operation: either `Self` or `Result<Self, Error>`
+    type Output;
+
+    /// Checked cast from `Src` to `Self`
+    fn cast(Src) -> Self::Output;
+}
+
+macro_rules! fns {
+    ($($ty:ident),+) => {
+        $(
+            /// Checked cast function
+            #[inline]
+            pub fn $ty<T>(x: T) -> <$ty as From<T>>::Output
+                where $ty: From<T>
+            {
+                <$ty as From<T>>::cast(x)
+            }
+         )+
+    }
+}
+
+fns!(f32, f64, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
+
+#[cfg(feature = "x128")]
+fns!(i128, u128);
+
+/// `$dst` can hold any value of `$src`
+macro_rules! promotion {
+    ($($src:ty => $($dst: ty),+);+;) => {
+        $(
+            $(
+                impl From<$src> for $dst {
+                    type Output = $dst;
+
+                    #[inline]
+                    fn cast(src: $src) -> $dst {
+                        src as $dst
+                    }
+                }
+            )+
+        )+
+    }
+}
+
+/// `$dst` can hold any positive value of `$src`
+macro_rules! half_promotion {
+    ($($src:ty => $($dst:ty),+);+;) => {
+        $(
+            $(
+                impl From<$src> for $dst {
+                    type Output = Result<$dst, Error>;
+
+                    #[inline]
+                    fn cast(src: $src) -> Self::Output {
+                        if src < 0 {
+                            Err(Error::Underflow)
+                        } else {
+                            Ok(src as $dst)
+                        }
+                    }
+                }
+            )+
+        )+
+    }
+}
+
+/// From an unsigned `$src` to a smaller `$dst`
+macro_rules! from_unsigned {
+    ($($src:ident => $($dst:ident),+);+;) => {
+        $(
+            $(
+                impl From<$src> for $dst {
+                    type Output = Result<$dst, Error>;
+
+                    #[inline]
+                    fn cast(src: $src) -> Self::Output {
+                        use core::$dst;
+
+                        if src > $dst::MAX as $src {
+                            Err(Error::Overflow)
+                        } else {
+                            Ok(src as $dst)
+                        }
+                    }
+                }
+            )+
+        )+
+    }
+}
+
+/// From a signed `$src` to a smaller `$dst`
+macro_rules! from_signed {
+    ($($src:ident => $($dst:ident),+);+;) => {
+        $(
+            $(
+                impl From<$src> for $dst {
+                    type Output = Result<$dst, Error>;
+
+                    #[inline]
+                    fn cast(src: $src) -> Self::Output {
+                        use core::$dst;
+
+                        Err(if src < $dst::MIN as $src {
+                            Error::Underflow
+                        } else if src > $dst::MAX as $src {
+                            Error::Overflow
+                        } else {
+                            return Ok(src as $dst);
+                        })
+                    }
+                }
+            )+
+        )+
+    }
+}
+
+/// From a float `$src` to an integer `$dst`
+macro_rules! from_float {
+    ($($src:ident => $($dst:ident),+);+;) => {
+        $(
+            $(
+                impl From<$src> for $dst {
+                    type Output = Result<$dst, Error>;
+
+                    #[inline]
+                    fn cast(src: $src) -> Self::Output {
+                        use core::{$dst, $src};
+
+                        Err(if src != src {
+                            Error::NaN
+                        } else if src == $src::INFINITY ||
+                            src == $src::NEG_INFINITY {
+                            Error::Infinite
+                        } else if src < $dst::MIN as $src {
+                            Error::Underflow
+                        } else if src > $dst::MAX as $src {
+                            Error::Overflow
+                        } else {
+                            return Ok(src as $dst);
+                        })
+                    }
+                }
+            )+
+        )+
+    }
+}
+
+/// From a float `$src` to an integer `$dst`, where $dst is large enough to contain
+/// all values of `$src`. We can't ever overflow here
+macro_rules! from_float_dst {
+    ($($src:ident => $($dst:ident),+);+;) => {
+        $(
+            $(
+                impl From<$src> for $dst {
+                    type Output = Result<$dst, Error>;
+
+                    #[inline]
+                    #[allow(unused_comparisons)]
+                    fn cast(src: $src) -> Self::Output {
+                        use core::{$dst, $src};
+
+                        Err(if src != src {
+                            Error::NaN
+                        } else if src == $src::INFINITY ||
+                            src == $src::NEG_INFINITY {
+                            Error::Infinite
+                        } else if ($dst::MIN == 0) && src < 0.0 {
+                            Error::Underflow
+                        } else {
+                            return Ok(src as $dst);
+                        })
+                    }
+                }
+            )+
+        )+
+    }
+}
+
+// PLAY TETRIS! ;-)
+
+#[cfg(target_pointer_width = "32")]
+mod _32 {
+    use {Error, From};
+
+    // Signed
+    promotion! {
+        i8    => f32, f64, i8, i16, i32, isize, i64;
+        i16   => f32, f64,     i16, i32, isize, i64;
+        i32   => f32, f64,          i32, isize, i64;
+        isize => f32, f64,          i32, isize, i64;
+        i64   => f32, f64,                      i64;
+    }
+
+    half_promotion! {
+        i8    =>                                     u8, u16, u32, usize, u64;
+        i16   =>                                         u16, u32, usize, u64;
+        i32   =>                                              u32, usize, u64;
+        isize =>                                              u32, usize, u64;
+        i64   =>                                                          u64;
+    }
+
+    from_signed! {
+
+        i16   =>           i8,                       u8;
+        i32   =>           i8, i16,                  u8, u16;
+        isize =>           i8, i16,                  u8, u16;
+        i64   =>           i8, i16, i32, isize,      u8, u16, u32, usize;
+    }
+
+    // Unsigned
+    promotion! {
+        u8    => f32, f64,     i16, i32, isize, i64, u8, u16, u32, usize, u64;
+        u16   => f32, f64,          i32, isize, i64,     u16, u32, usize, u64;
+        u32   => f32, f64,                      i64,          u32, usize, u64;
+        usize => f32, f64,                      i64,          u32, usize, u64;
+        u64   => f32, f64,                                                u64;
+    }
+
+    from_unsigned! {
+        u8    =>           i8;
+        u16   =>           i8, i16,                  u8;
+        u32   =>           i8, i16, i32, isize,      u8, u16;
+        usize =>           i8, i16, i32, isize,      u8, u16;
+        u64   =>           i8, i16, i32, isize, i64, u8, u16, u32, usize;
+    }
+
+    // Float
+    promotion! {
+        f32   => f32, f64;
+        f64   =>      f64;
+    }
+
+    from_float! {
+        f32   =>           i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
+        f64   =>           i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
+    }
+}
+
+#[cfg(target_pointer_width = "64")]
+mod _64 {
+    use {Error, From};
+
+    // Signed
+    promotion! {
+        i8    => f32, f64, i8, i16, i32, i64, isize;
+        i16   => f32, f64,     i16, i32, i64, isize;
+        i32   => f32, f64,          i32, i64, isize;
+        i64   => f32, f64,               i64, isize;
+        isize => f32, f64,               i64, isize;
+    }
+
+    half_promotion! {
+        i8    =>                                     u8, u16, u32, u64, usize;
+        i16   =>                                         u16, u32, u64, usize;
+        i32   =>                                              u32, u64, usize;
+        i64   =>                                                   u64, usize;
+        isize =>                                                   u64, usize;
+    }
+
+    from_signed! {
+
+        i16   =>           i8,                       u8;
+        i32   =>           i8, i16,                  u8, u16;
+        i64   =>           i8, i16, i32,             u8, u16, u32;
+        isize =>           i8, i16, i32,             u8, u16, u32;
+    }
+
+    // Unsigned
+    promotion! {
+        u8    => f32, f64,     i16, i32, i64, isize, u8, u16, u32, u64, usize;
+        u16   => f32, f64,          i32, i64, isize,     u16, u32, u64, usize;
+        u32   => f32, f64,               i64, isize,          u32, u64, usize;
+        u64   => f32, f64,                                         u64, usize;
+        usize => f32, f64,                                         u64, usize;
+    }
+
+    from_unsigned! {
+        u8    =>           i8;
+        u16   =>           i8, i16,                  u8;
+        u32   =>           i8, i16, i32,             u8, u16;
+        u64   =>           i8, i16, i32, i64, isize, u8, u16, u32;
+        usize =>           i8, i16, i32, i64, isize, u8, u16, u32;
+    }
+
+    // Float
+    promotion! {
+        f32  => f32, f64;
+        f64  =>      f64;
+    }
+
+    from_float! {
+        f32  =>           i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
+        f64  =>           i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
+    }
+}
+
+#[cfg(feature = "x128")]
+mod _x128 {
+    use {Error, From};
+
+    // Signed
+    promotion! {
+        i8    =>                              i128;
+        i16   =>                              i128;
+        i32   =>                              i128;
+        i64   =>                              i128;
+        isize =>                              i128;
+        i128  => f32, f64,                    i128;
+    }
+
+    half_promotion! {
+        i8    =>                                                              u128;
+        i16   =>                                                              u128;
+        i32   =>                                                              u128;
+        i64   =>                                                              u128;
+        isize =>                                                              u128;
+        i128  =>                                                              u128;
+    }
+
+    from_signed! {
+        i128  =>           i8, i16, i32, i64,       isize, u8, u16, u32, u64,       usize;
+    }
+
+    // Unsigned
+    promotion! {
+        u8    =>                              i128,                           u128;
+        u16   =>                              i128,                           u128;
+        u32   =>                              i128,                           u128;
+        u64   =>                              i128,                           u128;
+        usize =>                              i128,                           u128;
+        u128  =>      f64,                                                    u128;
+    }
+
+    from_unsigned! {
+        u128 => f32,       i8, i16, i32, i64, i128, isize, u8, u16, u32, u64,       usize;
+    }
+
+    // Float
+    from_float! {
+        f32  => i128;
+        f64  => i128, u128;
+    }
+    from_float_dst! {
+        f32  => u128;
+    }
+}
+
+// The missing piece
+impl From<f64> for f32 {
+    type Output = Result<f32, Error>;
+
+    #[inline]
+    fn cast(src: f64) -> Self::Output {
+        use core::{f32, f64};
+
+        if src != src || src == f64::INFINITY || src == f64::NEG_INFINITY {
+            Ok(src as f32)
+        } else if src < f32::MIN as f64 {
+            Err(Error::Underflow)
+        } else if src > f32::MAX as f64 {
+            Err(Error::Overflow)
+        } else {
+            Ok(src as f32)
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cast/src/test.rs
@@ -0,0 +1,191 @@
+// If `src` can be promoted to `$dst`, then it must be Ok to cast `dst` back to
+// `$src`
+macro_rules! promote_and_back {
+    ($($src:ident => $($dst:ident),+);+;) => {
+        mod demoting_to {
+            $(
+                mod $src {
+                    mod from {
+                        use From;
+
+                        $(
+                            quickcheck! {
+                                fn $dst(src: $src) -> bool {
+                                    $src::cast($dst::cast(src)).is_ok()
+                                }
+                            }
+                         )+
+                    }
+                }
+             )+
+        }
+    }
+}
+
+#[cfg(target_pointer_width = "32")]
+promote_and_back! {
+    i8    => f32, f64,     i16, i32, isize, i64                          ;
+    i16   => f32, f64,          i32, isize, i64                          ;
+    i32   => f32, f64,                      i64                          ;
+    isize => f32, f64,                      i64                          ;
+    i64   => f32, f64                                                    ;
+    u8    => f32, f64,     i16, i32, isize, i64,     u16, u32, usize, u64;
+    u16   => f32, f64,          i32, isize, i64,          u32, usize, u64;
+    u32   => f32, f64,                      i64,                      u64;
+    usize => f32, f64,                      i64,                      u64;
+    u64   => f32, f64                                                    ;
+}
+
+#[cfg(target_pointer_width = "64")]
+promote_and_back! {
+    i8    => f32, f64,     i16, i32, i64, isize                          ;
+    i16   => f32, f64,          i32, i64, isize                          ;
+    i32   => f32, f64,               i64, isize                          ;
+    i64   => f32, f64                                                    ;
+    isize => f32, f64                                                    ;
+    u8    => f32, f64,     i16, i32, i64, isize,     u16, u32, u64, usize;
+    u16   => f32, f64,          i32, i64, isize,          u32, u64, usize;
+    u32   => f32, f64,               i64, isize,               u64, usize;
+    u64   => f32, f64                                                    ;
+    usize => f32, f64                                                    ;
+}
+
+// TODO uncomment this once quickcheck supports Arbitrary for i128/u128
+// https://github.com/BurntSushi/quickcheck/issues/162
+/*#[cfg(feature = "x128")]
+promote_and_back! {
+    i8    =>           i128      ;
+    i16   =>           i128      ;
+    i32   =>           i128      ;
+    isize =>           i128      ;
+    i64   =>           i128      ;
+    i128  => f32, f64            ;
+    u8    =>           i128, u128;
+    u16   =>           i128, u128;
+    u32   =>           i128, u128;
+    usize =>           i128, u128;
+    u64   =>           i128, u128;
+    u128  => f32, f64            ;
+}*/
+
+// If it's Ok to cast `src` to `$dst`, it must also be Ok to cast `dst` back to
+// `$src`
+macro_rules! symmetric_cast_between {
+    ($($src:ident => $($dst:ident),+);+;) => {
+        mod symmetric_cast_between {
+            $(
+                mod $src {
+                    mod and {
+                        use quickcheck::TestResult;
+
+                        use From;
+
+                        $(
+                            quickcheck! {
+                                fn $dst(src: $src) -> TestResult {
+                                    if let Ok(dst) = $dst::cast(src) {
+                                        TestResult::from_bool(
+                                            $src::cast(dst).is_ok())
+                                    } else {
+                                        TestResult::discard()
+                                    }
+                                }
+                            }
+                         )+
+                    }
+                }
+             )+
+        }
+    }
+}
+
+#[cfg(target_pointer_width = "32")]
+symmetric_cast_between! {
+    u8    =>           i8                      ;
+    u16   =>           i8, i16                 ;
+    u32   =>           i8, i16, i32            ;
+    usize =>           i8, i16, i32            ;
+    u64   =>           i8, i16, i32, i64, isize;
+}
+
+#[cfg(target_pointer_width = "64")]
+symmetric_cast_between! {
+    u8    =>           i8                      ;
+    u16   =>           i8, i16                 ;
+    u32   =>           i8, i16, i32            ;
+    u64   =>           i8, i16, i32, i64, isize;
+    usize =>           i8, i16, i32, i64, isize;
+}
+
+// TODO uncomment this once quickcheck supports Arbitrary for i128/u128
+// https://github.com/BurntSushi/quickcheck/issues/162
+/*#[cfg(feature = "x128")]
+symmetric_cast_between! {
+    u128  => i8, i16, i32, isize, i64, i128;
+}*/
+
+macro_rules! from_float {
+    ($($src:ident => $($dst:ident),+);+;) => {
+        $(
+            mod $src {
+                mod inf {
+                    mod to {
+                        use {Error, From};
+
+                        $(
+                            #[test]
+                            fn $dst() {
+                                let _0: $src = 0.;
+                                let _1: $src = 1.;
+                                let inf = _1 / _0;
+                                let neg_inf = -_1 / _0;
+
+                                assert_eq!($dst::cast(inf),
+                                           Err(Error::Infinite));
+                                assert_eq!($dst::cast(neg_inf),
+                                           Err(Error::Infinite));
+                            }
+                         )+
+                    }
+                }
+
+                mod nan {
+                    mod to {
+                        use {Error, From};
+
+                        $(
+                            #[test]
+                            fn $dst() {
+                                let _0: $src = 0.;
+                                let nan = _0 / _0;
+
+                                assert_eq!($dst::cast(nan),
+                                           Err(Error::NaN));
+                            }
+                         )+
+                    }
+                }
+            }
+         )+
+    }
+}
+
+from_float! {
+    f32 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
+    f64 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
+}
+
+// TODO uncomment this once quickcheck supports Arbitrary for i128/u128
+// https://github.com/BurntSushi/quickcheck/issues/162
+/*#[cfg(feature = "x128")]
+from_float! {
+    f32 => i128, u128;
+    f64 => i128, u128;
+}*/
+
+#[test]
+#[cfg(feature = "x128")]
+fn test_fl_conversion() {
+    use u128;
+    assert_eq!(u128(42.0f32), Ok(42));
+}
--- 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":"8c219cb955621bfeb89eb1175dc9d810dc98323ccb54360e623425308c970e72","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"af367c67340fa7f6fb9a35b0aa637dcf303957f7ae7427a5f4f6356801c8bb04","src/lib.rs":"01f8c9b8a077975c8f0803f793b8c20b583aaef2dc1f4400b9d90ba132ff4133","src/map.rs":"77eb9fd2ffdaafaf4daea609602a0c775c5012efae21c03547f63653271da163","src/node.rs":"309609acc70f1ce6be2f3c964430d23c0680bd7a647afab837a2aedc06235531","src/path.rs":"25326bacbb99189e873cb70e770f21c13fdef0fb2cd20f484830386fc4c75c6a","src/pool.rs":"196216124922dc42708a3aa944e98b6a57ef9bb770dab7e01f154b6382cab021","src/set.rs":"d4ff99fe51de9eefb4c774e919259d952ab5dde4dd3b99bd9974e4eedbb28938"},"package":"7b2d527b241af388ff017d72f2b0b323929a70cf97342c6ec1534e3b0f4dfaa0"}
\ No newline at end of file
+{"files":{"Cargo.toml":"591c2da0dad7eafa9aff626400b38e0b08927df674292ca6774f60e2bc02ac32","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"af367c67340fa7f6fb9a35b0aa637dcf303957f7ae7427a5f4f6356801c8bb04","src/lib.rs":"eb691a3bc63c3ed8a64d564cf656fdc56c62ade8b7882efd7b987353acc627bd","src/map.rs":"77eb9fd2ffdaafaf4daea609602a0c775c5012efae21c03547f63653271da163","src/node.rs":"309609acc70f1ce6be2f3c964430d23c0680bd7a647afab837a2aedc06235531","src/path.rs":"25326bacbb99189e873cb70e770f21c13fdef0fb2cd20f484830386fc4c75c6a","src/pool.rs":"196216124922dc42708a3aa944e98b6a57ef9bb770dab7e01f154b6382cab021","src/set.rs":"d4ff99fe51de9eefb4c774e919259d952ab5dde4dd3b99bd9974e4eedbb28938"},"package":"40f8ff24e9a6c89b8a846b14df9a34d2cac17cea7bdb5c81ed6b4744ee0e38bf"}
\ No newline at end of file
--- a/third_party/rust/cranelift-bforest/Cargo.toml
+++ b/third_party/rust/cranelift-bforest/Cargo.toml
@@ -7,27 +7,27 @@
 #
 # 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]
 name = "cranelift-bforest"
-version = "0.25.0"
+version = "0.26.0"
 authors = ["The Cranelift Project Developers"]
 description = "A forest of B+-trees"
 documentation = "https://cranelift.readthedocs.io/"
 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.25.0"
+version = "0.26.0"
 default-features = false
 
 [features]
 default = ["std"]
 std = ["cranelift-entity/std"]
 [badges.maintenance]
 status = "experimental"
 
--- a/third_party/rust/cranelift-bforest/src/lib.rs
+++ b/third_party/rust/cranelift-bforest/src/lib.rs
@@ -11,35 +11,32 @@
 //! - Keys and values are expected to be small and copyable. We optimize for 32-bit types.
 //! - A comparator object is used to compare keys, allowing smaller "context free" keys.
 //! - Empty trees have a very small 32-bit footprint.
 //! - All the trees in a forest can be cleared in constant time.
 
 #![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
 #![warn(unused_import_braces)]
 #![cfg_attr(feature = "std", warn(unstable_features))]
-#![cfg_attr(
-    feature = "clippy",
-    plugin(clippy(conf_file = "../../clippy.toml"))
-)]
+#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
 #![cfg_attr(
     feature = "cargo-clippy",
     allow(new_without_default, new_without_default_derive)
 )]
 #![cfg_attr(
     feature = "cargo-clippy",
     warn(
-        float_arithmetic,
-        mut_mut,
-        nonminimal_bool,
-        option_map_unwrap_or,
-        option_map_unwrap_or_else,
-        print_stdout,
-        unicode_not_nfc,
-        use_self
+        clippy::float_arithmetic,
+        clippy::mut_mut,
+        clippy::nonminimal_bool,
+        clippy::option_map_unwrap_or,
+        clippy::option_map_unwrap_or_else,
+        clippy::print_stdout,
+        clippy::unicode_not_nfc,
+        clippy::use_self
     )
 )]
 // Turns on no_std and alloc features if std is not available.
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(not(feature = "std"), feature(alloc))]
 
 /// This replaces `std` in builds with `core`.
 #[cfg(not(feature = "std"))]
--- 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":"0e4050095c283d025e30ccba0e3d9753b63340604b6373e4ea06d221eb3a97e1","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/base/mod.rs":"9320dfed2250bdb0347e01862b2ff7bf7db78920dae1719834b374de11131e87","src/base/types.rs":"a3e449db1f515d268f3ad21301740ba415444d399f8433dbc48979f78557f66a","src/cdsl/isa.rs":"b34150e2658dd7ab30e9846dece89f56a0acbfff0341927cec06bf0de1d9b419","src/cdsl/mod.rs":"311726d7e4ad9278eab301fd4f6e31e697b7d4260733c6a00fe39cd61db977d3","src/cdsl/regs.rs":"41cca844b390bba3ceefa147e7b0dec170aba2b5759a41ecb5b9cd7f2cc59f60","src/cdsl/types.rs":"f9756e483329f00a1d8a15e30bc05e8d4c8fa71ff1f649b808528ddeb5fbdfea","src/error.rs":"5110a4e3c1e97396ba02d9f5abbb8af4b586f0cc4d33a5c2473f1718cc4bef05","src/gen_registers.rs":"3d38ff5b0c6183209d4ba84bd1f14b1d84bea697c0589471aa5ce4abc209f20b","src/gen_types.rs":"5eb4e9bd0fda7f7644bb2428045f0bf16f2b698ff32cadcbbf7f2c7669f18de3","src/isa/arm32/mod.rs":"ceb8290344a245f11eb541d60c2678ddfe7beaf60cde491c22fd48090fa210ba","src/isa/arm64/mod.rs":"db7f828940372a21c62cf6f5aefca7cc05c974ba731b8db602da0a336e29ea1e","src/isa/mod.rs":"36c001f8ecdea4aaf8cadcad4f91fe79a82806e4f43182e216c2fa99cecf55dc","src/isa/riscv/mod.rs":"aecb127f8c06970a00dde8ad772fdebe5497b6e7f342fa2a0e859355ae027055","src/isa/x86/mod.rs":"2b3a181a226d66a8faf855d517fb4a13554a31020f6b8875ba75a2fe650f378a","src/lib.rs":"dd97d73d41ffee2d2cc62705f9f6f7ed6b9af982aff8d1fafb72590e097c513c","src/srcgen.rs":"abe118fb41a643ffc63577cc4b62de9a2286e1eeb34b95bff79648d0ea582886"},"package":"963262697a05d9aa63ca40f4670a7243e4525f4a098e10d654c3f5143fcef686"}
\ No newline at end of file
+{"files":{"Cargo.toml":"cdd4cff2d17ca34e3305e9fcf1e8277b0e9987e7369ce70da597e3957e56e408","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/base/mod.rs":"559075f0b76a744dd36224e06ff1c2e28ee70aaca9442fc724b116e37028ac52","src/base/settings.rs":"9cbe9bdd5141b9175074370a9b1d28f371249d586e999a31f76631739bf09c13","src/base/types.rs":"a3e449db1f515d268f3ad21301740ba415444d399f8433dbc48979f78557f66a","src/cdsl/isa.rs":"5c9a8173466e69d105245396abd342251eb00e704ab13f179ba1567b339f47e1","src/cdsl/mod.rs":"66ac1b5d095e431bcab88c4b9c5b1492a5d1ca87bcb9c9c3e544ede05b2ba925","src/cdsl/regs.rs":"41cca844b390bba3ceefa147e7b0dec170aba2b5759a41ecb5b9cd7f2cc59f60","src/cdsl/settings.rs":"724a4bd7cfd0608cfc8751ee7c38dbed90b91cfafd34585ed7df953807932794","src/cdsl/types.rs":"78f476f5f700697b94f2d2f00049af8684d3b27afc5684b2c87ea517aeb77e85","src/constant_hash.rs":"6522f86ebfd44efe9b47256d822d0e49d641ccdbb4fcc61d57bb94e6d52702c1","src/error.rs":"5110a4e3c1e97396ba02d9f5abbb8af4b586f0cc4d33a5c2473f1718cc4bef05","src/gen_registers.rs":"9bd381da256c19724964c6445db4fbac80d91174266dccfc5d3b72497a5332c9","src/gen_settings.rs":"a827a90cc1db7345e8081635169b77be497494e558c6e985eaa654386d5e8e48","src/gen_types.rs":"5eb4e9bd0fda7f7644bb2428045f0bf16f2b698ff32cadcbbf7f2c7669f18de3","src/isa/arm32/mod.rs":"b4049ff95164bbf244b6727e16974383e4d58c5be750f7ded3ef8cfe113e5373","src/isa/arm64/mod.rs":"759adfd4dd5c885644de26386797211efc82c5b47f2e147a58d57d24608b9cfb","src/isa/mod.rs":"d3a87bc45173f3743f25d3a004a9b40966c12feb7d95d794b5918bb9ccb988ac","src/isa/riscv/mod.rs":"6e3d96c48e9c289a13912d7f777bde805be327e4a56677d3f3bccf440ae4a09b","src/isa/x86/mod.rs":"e38c60a9d1aba3e5a48052c6185bab3f2b039e9cce639826becfe9f853c41499","src/lib.rs":"c4bfd9d2973e4a382f7a1ce8389cc1604aeba8478432a542ff3f1c24412c5b41","src/srcgen.rs":"e358b6232f0820e6e4525cdbe216206996ae5eb16a1df7a5fe41e3ce2e25b633","src/unique_table.rs":"f6041df1fa85f2a1ee914b84791e80165a0858a6253c212eaa99ff67cb56af26"},"package":"014c23ed3ebdc8377d41540af638245207dd169f421df042dfccc867465734ed"}
\ No newline at end of file
--- a/third_party/rust/cranelift-codegen-meta/Cargo.toml
+++ b/third_party/rust/cranelift-codegen-meta/Cargo.toml
@@ -7,21 +7,21 @@
 #
 # 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]
 name = "cranelift-codegen-meta"
-version = "0.25.0"
+version = "0.26.0"
 authors = ["The Cranelift Project Developers"]
 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.25.0"
+version = "0.26.0"
 [badges.maintenance]
 status = "experimental"
 
 [badges.travis-ci]
 repository = "CraneStation/cranelift"
--- a/third_party/rust/cranelift-codegen-meta/src/base/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/base/mod.rs
@@ -1,3 +1,4 @@
 //! Definitions for the base Cranelift language.
 
+pub mod settings;
 pub mod types;
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/base/settings.rs
@@ -0,0 +1,164 @@
+use cdsl::settings::{SettingGroup, SettingGroupBuilder};
+
+pub fn generate() -> SettingGroup {
+    let mut settings = SettingGroupBuilder::new("shared");
+
+    settings.add_enum(
+        "opt_level",
+        r#"
+        Optimization level:
+
+        - default: Very profitable optimizations enabled, none slow.
+        - best: Enable all optimizations
+        - fastest: Optimize for compile time by disabling most optimizations.
+        "#,
+        vec!["default", "best", "fastest"],
+    );
+
+    settings.add_bool(
+        "enable_verifier",
+        r#"
+        Run the Cranelift IR verifier at strategic times during compilation.
+
+        This makes compilation slower but catches many bugs. The verifier is
+        disabled by default, except when reading Cranelift IR from a text file.
+        "#,
+        true,
+    );
+
+    // Note that Cranelift doesn't currently need an is_pie flag, because PIE is
+    // just PIC where symbols can't be pre-empted, which can be expressed with the
+    // `colocated` flag on external functions and global values.
+    settings.add_bool(
+        "is_pic",
+        "Enable Position-Independent Code generation",
+        false,
+    );
+
+    settings.add_bool(
+        "colocated_libcalls",
+        r#"
+            Use colocated libcalls.
+
+            Generate code that assumes that libcalls can be declared "colocated",
+            meaning they will be defined along with the current function, such that
+            they can use more efficient addressing.
+            "#,
+        false,
+    );
+
+    settings.add_bool(
+        "avoid_div_traps",
+        r#"
+            Generate explicit checks around native division instructions to avoid
+            their trapping.
+
+            This is primarily used by SpiderMonkey which doesn't install a signal
+            handler for SIGFPE, but expects a SIGILL trap for division by zero.
+
+            On ISAs like ARM where the native division instructions don't trap,
+            this setting has no effect - explicit checks are always inserted.
+            "#,
+        false,
+    );
+
+    settings.add_bool(
+        "enable_float",
+        r#"
+            Enable the use of floating-point instructions
+
+            Disabling use of floating-point instructions is not yet implemented.
+            "#,
+        true,
+    );
+
+    settings.add_bool(
+        "enable_nan_canonicalization",
+        r#"
+            Enable NaN canonicalization
+
+            This replaces NaNs with a single canonical value, for users requiring
+            entirely deterministic WebAssembly computation. This is not required
+            by the WebAssembly spec, so it is not enabled by default.
+            "#,
+        false,
+    );
+
+    settings.add_bool("enable_simd", "Enable the use of SIMD instructions.", true);
+
+    settings.add_bool(
+        "enable_atomics",
+        "Enable the use of atomic instructions",
+        true,
+    );
+
+    // Settings specific to the `baldrdash` calling convention.
+
+    settings.add_num(
+        "baldrdash_prologue_words",
+        r#"
+            Number of pointer-sized words pushed by the baldrdash prologue.
+
+            Functions with the `baldrdash` calling convention don't generate their
+            own prologue and epilogue. They depend on externally generated code
+            that pushes a fixed number of words in the prologue and restores them
+            in the epilogue.
+
+            This setting configures the number of pointer-sized words pushed on the
+            stack when the Cranelift-generated code is entered. This includes the
+            pushed return address on x86.
+            "#,
+        0,
+    );
+
+    // BaldrMonkey requires that not-yet-relocated function addresses be encoded
+    // as all-ones bitpatterns.
+    settings.add_bool(
+        "allones_funcaddrs",
+        "Emit not-yet-relocated function addresses as all-ones bit patterns.",
+        false,
+    );
+
+    // Stack probing options.
+
+    settings.add_bool(
+        "probestack_enabled",
+        r#"
+            Enable the use of stack probes, for calling conventions which support this
+            functionality.
+            "#,
+        true,
+    );
+
+    settings.add_bool(
+        "probestack_func_adjusts_sp",
+        r#"
+            Set this to true of the stack probe function modifies the stack pointer
+            itself.
+            "#,
+        false,
+    );
+
+    settings.add_num(
+        "probestack_size_log2",
+        r#"
+            The log2 of the size of the stack guard region.
+
+            Stack frames larger than this size will have stack overflow checked
+            by calling the probestack function.
+
+            The default is 12, which translates to a size of 4096.
+            "#,
+        12,
+    );
+
+    // Jump table options.
+
+    settings.add_bool(
+        "jump_tables_enabled",
+        "Enable the use of jump tables in generated machine code.",
+        true,
+    );
+
+    settings.finish()
+}
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/isa.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/isa.rs
@@ -1,38 +1,41 @@
 use cranelift_entity::PrimaryMap;
 
 use super::regs::{
     RegBank, RegBankBuilder, RegBankIndex, RegClass, RegClassBuilder, RegClassIndex, RegClassProto,
 };
+use super::settings::SettingGroup;
 
 pub struct TargetIsa {
     pub name: &'static str,
     pub reg_banks: PrimaryMap<RegBankIndex, RegBank>,
     pub reg_classes: PrimaryMap<RegClassIndex, RegClass>,
+    pub settings: SettingGroup,
 }
 
 impl TargetIsa {
-    pub fn new(name: &'static str) -> Self {
+    pub fn new(name: &'static str, settings: SettingGroup) -> Self {
         Self {
             name,
             reg_banks: PrimaryMap::new(),
             reg_classes: PrimaryMap::new(),
+            settings,
         }
     }
 }
 
 pub struct TargetIsaBuilder {
     isa: TargetIsa,
 }
 
 impl TargetIsaBuilder {
-    pub fn new(name: &'static str) -> Self {
+    pub fn new(name: &'static str, settings: SettingGroup) -> Self {
         Self {
-            isa: TargetIsa::new(name),
+            isa: TargetIsa::new(name, settings),
         }
     }
 
     pub fn add_reg_bank(&mut self, builder: RegBankBuilder) -> RegBankIndex {
         let first_unit = if self.isa.reg_banks.len() == 0 {
             0
         } else {
             let last = &self.isa.reg_banks.last().unwrap();
@@ -145,26 +148,25 @@ impl TargetIsaBuilder {
                     }
 
                     // Classes must be topologically ordered, so the intersection can't be the
                     // superclass.
                     assert!(intersect != rc1_mask);
 
                     // If the intersection is the second one, then it must be a subclass.
                     if intersect == rc2_mask {
-                        assert!(
-                            self.isa
-                                .reg_classes
-                                .get(*i1)
-                                .unwrap()
-                                .subclasses
-                                .iter()
-                                .find(|x| **x == *i2)
-                                .is_some()
-                        );
+                        assert!(self
+                            .isa
+                            .reg_classes
+                            .get(*i1)
+                            .unwrap()
+                            .subclasses
+                            .iter()
+                            .find(|x| **x == *i2)
+                            .is_some());
                     }
                 }
             }
         }
 
         // This limit should be coordinated with the `RegClassMask` and `RegClassIndex` types in
         // isa/registers.rs of the non-meta code.
         assert!(
@@ -176,14 +178,15 @@ impl TargetIsaBuilder {
         // kept in sync with the MAX_TRACKED_TOPRCS constant in isa/registers.rs of the non-meta
         // code.
         let num_toplevel = self
             .isa
             .reg_classes
             .values()
             .filter(|x| {
                 x.toprc == x.index && self.isa.reg_banks.get(x.bank).unwrap().pressure_tracking
-            }).count();
+            })
+            .count();
         assert!(num_toplevel <= 4, "Too many top-level register classes");
 
         self.isa
     }
 }
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/mod.rs
@@ -1,19 +1,47 @@
 //! Cranelift DSL classes.
 //!
 //! This module defines the classes that are used to define Cranelift
 //! instructions and other entities.
 
 pub mod isa;
 pub mod regs;
+pub mod settings;
 pub mod types;
 
+/// 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) => {
+        $a.into()
+    };
+}
+
+#[macro_export]
+macro_rules! preset {
+    () => {
+        vec![]
+    };
+    ($($x:ident)&&*) => {
+        {
+            let mut v = Vec::new();
+            $(
+                v.push($x.into());
+            )*
+            v
+        }
+    };
+}
+
 /// Convert the string `s` to CamelCase.
-fn _camel_case(s: &str) -> String {
+pub fn camel_case(s: &str) -> String {
     let mut output_chars = String::with_capacity(s.len());
 
     let mut capitalize = true;
     for curr_char in s.chars() {
         if curr_char == '_' {
             capitalize = true;
         } else {
             if capitalize {
@@ -25,16 +53,16 @@ fn _camel_case(s: &str) -> String {
         }
     }
 
     output_chars
 }
 
 #[cfg(test)]
 mod tests {
-    use super::_camel_case as camel_case;
+    use super::camel_case;
 
     #[test]
     fn camel_case_works() {
         assert_eq!(camel_case("x"), "X");
         assert_eq!(camel_case("camel_case"), "CamelCase");
     }
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/settings.rs
@@ -0,0 +1,386 @@
+use std::iter;
+
+#[derive(Clone, Copy, Hash, PartialEq, Eq)]
+pub struct BoolSettingIndex(usize);
+
+#[derive(Hash, PartialEq, Eq)]
+pub struct BoolSetting {
+    pub default: bool,
+    pub bit_offset: u8,
+    pub predicate_number: u8,
+}
+
+#[derive(Hash, PartialEq, Eq)]
+pub enum SpecificSetting {
+    Bool(BoolSetting),
+    Enum(Vec<&'static str>),
+    Num(u8),
+}
+
+#[derive(Hash, PartialEq, Eq)]
+pub struct Setting {
+    pub name: &'static str,
+    pub comment: &'static str,
+    pub specific: SpecificSetting,
+    pub byte_offset: u8,
+}
+
+impl Setting {
+    pub fn default_byte(&self) -> u8 {
+        match self.specific {
+            SpecificSetting::Bool(BoolSetting {
+                default,
+                bit_offset,
+                ..
+            }) => {
+                if default {
+                    1 << bit_offset
+                } else {
+                    0
+                }
+            }
+            SpecificSetting::Enum(_) => 0,
+            SpecificSetting::Num(default) => default,
+        }
+    }
+
+    fn byte_for_value(&self, v: bool) -> u8 {
+        match self.specific {
+            SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
+                if v {
+                    1 << bit_offset
+                } else {
+                    0
+                }
+            }
+            _ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
+        }
+    }
+
+    fn byte_mask(&self) -> u8 {
+        match self.specific {
+            SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => 1 << bit_offset,
+            _ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
+        }
+    }
+}
+
+#[derive(Hash, PartialEq, Eq)]
+pub struct PresetIndex(usize);
+
+#[derive(Hash, PartialEq, Eq)]
+pub enum PresetType {
+    BoolSetting(BoolSettingIndex),
+    OtherPreset(PresetIndex),
+}
+
+impl Into<PresetType> for BoolSettingIndex {
+    fn into(self) -> PresetType {
+        PresetType::BoolSetting(self)
+    }
+}
+impl Into<PresetType> for PresetIndex {
+    fn into(self) -> PresetType {
+        PresetType::OtherPreset(self)
+    }
+}
+
+#[derive(Hash, PartialEq, Eq)]
+pub struct Preset {
+    pub name: &'static str,
+    values: Vec<BoolSettingIndex>,
+}
+
+impl Preset {
+    pub fn layout(&self, group: &SettingGroup) -> Vec<(u8, u8)> {
+        let mut layout: Vec<(u8, u8)> = iter::repeat((0, 0))
+            .take(group.settings_size as usize)
+            .collect();
+        for bool_index in &self.values {
+            let setting = &group.settings[bool_index.0];
+            let mask = setting.byte_mask();
+            let val = setting.byte_for_value(true);
+            assert!((val & !mask) == 0);
+            let (l_mask, l_val) = layout.get_mut(setting.byte_offset as usize).unwrap();
+            *l_mask |= mask;
+            *l_val = (*l_val & !mask) | val;
+        }
+        layout
+    }
+}
+
+pub struct SettingGroup {
+    pub name: &'static str,
+    pub settings: Vec<Setting>,
+    pub bool_start_byte_offset: u8,
+    pub settings_size: u8,
+    pub presets: Vec<Preset>,
+    pub predicates: Vec<Predicate>,
+}
+
+impl SettingGroup {
+    fn num_bool_settings(&self) -> u8 {
+        self.settings
+            .iter()
+            .filter(|s| {
+                if let SpecificSetting::Bool(_) = s.specific {
+                    true
+                } else {
+                    false
+                }
+            })
+            .count() as u8
+    }
+
+    pub fn byte_size(&self) -> u8 {
+        let num_predicates = self.num_bool_settings() + (self.predicates.len() as u8);
+        self.bool_start_byte_offset + (num_predicates + 7) / 8
+    }
+
+    pub fn get_bool(&self, name: &'static str) -> (BoolSettingIndex, &Self) {
+        for (i, s) in self.settings.iter().enumerate() {
+            if let SpecificSetting::Bool(_) = s.specific {
+                if s.name == name {
+                    return (BoolSettingIndex(i), self);
+                }
+            }
+        }
+        panic!("Should have found bool setting by name.");
+    }
+}
+
+/// This is the basic information needed to track the specific parts of a setting when building
+/// them.
+pub enum ProtoSpecificSetting {
+    Bool(bool, u8),
+    Enum(Vec<&'static str>),
+    Num(u8),
+}
+
+/// This is the information provided during building for a setting.
+struct ProtoSetting {
+    name: &'static str,
+    comment: &'static str,
+    specific: ProtoSpecificSetting,
+}
+
+#[derive(Hash, PartialEq, Eq)]
+pub enum PredicateNode {
+    OwnedBool(BoolSettingIndex),
+    SharedBool(&'static str, &'static str),
+    And(Box<PredicateNode>, Box<PredicateNode>),
+}
+
+impl Into<PredicateNode> for BoolSettingIndex {
+    fn into(self) -> PredicateNode {
+        PredicateNode::OwnedBool(self)
+    }
+}
+impl<'a> Into<PredicateNode> for (BoolSettingIndex, &'a SettingGroup) {
+    fn into(self) -> PredicateNode {
+        let (index, group) = (self.0, self.1);
+        let setting = &group.settings[index.0];
+        PredicateNode::SharedBool(group.name, setting.name)
+    }
+}
+
+impl PredicateNode {
+    fn render(&self, group: &SettingGroup) -> String {
+        match self {
+            PredicateNode::OwnedBool(bool_setting_index) => format!(
+                "{}.{}()",
+                group.name, group.settings[bool_setting_index.0].name
+            ),
+            PredicateNode::SharedBool(group_name, bool_name) => {
+                format!("{}.{}()", group_name, bool_name)
+            }
+            PredicateNode::And(lhs, rhs) => {
+                format!("{} && {}", lhs.render(group), rhs.render(group))
+            }
+        }
+    }
+}
+
+pub struct Predicate {
+    pub name: &'static str,
+    node: PredicateNode,
+    pub number: u8,
+}
+
+impl Predicate {
+    pub fn render(&self, group: &SettingGroup) -> String {
+        self.node.render(group)
+    }
+}
+
+pub struct SettingGroupBuilder {
+    name: &'static str,
+    settings: Vec<ProtoSetting>,
+    presets: Vec<Preset>,
+    predicates: Vec<Predicate>,
+    predicate_number: u8,
+}
+
+impl SettingGroupBuilder {
+    pub fn new(name: &'static str) -> Self {
+        Self {
+            name,
+            settings: Vec::new(),
+            presets: Vec::new(),
+            predicates: Vec::new(),
+            predicate_number: 0,
+        }
+    }
+
+    fn add_setting(
+        &mut self,
+        name: &'static str,
+        comment: &'static str,
+        specific: ProtoSpecificSetting,
+    ) {
+        self.settings.push(ProtoSetting {
+            name,
+            comment,
+            specific,
+        })
+    }
+
+    pub fn add_bool(
+        &mut self,
+        name: &'static str,
+        comment: &'static str,
+        default: bool,
+    ) -> BoolSettingIndex {
+        assert!(
+            self.predicates.len() == 0,
+            "predicates must be added after the boolean settings"
+        );
+        let predicate_number = self.predicate_number;
+        self.predicate_number += 1;
+        self.add_setting(
+            name,
+            comment,
+            ProtoSpecificSetting::Bool(default, predicate_number),
+        );
+        BoolSettingIndex(self.settings.len() - 1)
+    }
+
+    pub fn add_enum(
+        &mut self,
+        name: &'static str,
+        comment: &'static str,
+        values: Vec<&'static str>,
+    ) {
+        self.add_setting(name, comment, ProtoSpecificSetting::Enum(values));
+    }
+
+    pub fn add_num(&mut self, name: &'static str, comment: &'static str, default: u8) {
+        self.add_setting(name, comment, ProtoSpecificSetting::Num(default));
+    }
+
+    pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) {
+        let number = self.predicate_number;
+        self.predicate_number += 1;
+        self.predicates.push(Predicate { name, node, number });
+    }
+
+    pub fn add_preset(&mut self, name: &'static str, args: Vec<PresetType>) -> PresetIndex {
+        let mut values = Vec::new();
+        for arg in args {
+            match arg {
+                PresetType::OtherPreset(index) => {
+                    values.extend(self.presets[index.0].values.iter());
+                }
+                PresetType::BoolSetting(index) => values.push(index),
+            }
+        }
+        self.presets.push(Preset { name, values });
+        PresetIndex(self.presets.len() - 1)
+    }
+
+    /// Compute the layout of the byte vector used to represent this settings
+    /// group.
+    ///
+    /// The byte vector contains the following entries in order:
+    ///
+    /// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
+    /// 2. `BoolSetting` settings.
+    /// 3. Precomputed named predicates.
+    /// 4. Other numbered predicates, including anonymous predicates and parent
+    ///    predicates that need to be accessible by number.
+    ///
+    /// Set `self.settings_size` to the length of the byte vector prefix that
+    /// contains the settings. All bytes after that are computed, not
+    /// configured.
+    ///
+    /// Set `self.boolean_offset` to the beginning of the numbered predicates,
+    /// 2. in the list above.
+    ///
+    /// Assign `byte_offset` and `bit_offset` fields in all settings.
+    ///
+    /// After calling this method, no more settings can be added, but
+    /// additional predicates can be made accessible with `number_predicate()`.
+    pub fn finish(self) -> SettingGroup {
+        let mut group = SettingGroup {
+            name: self.name,
+            settings: Vec::new(),
+            bool_start_byte_offset: 0,
+            settings_size: 0,
+            presets: Vec::new(),
+            predicates: Vec::new(),
+        };
+
+        let mut byte_offset = 0;
+
+        // Assign the non-boolean settings first.
+        for s in &self.settings {
+            let specific = match s.specific {
+                ProtoSpecificSetting::Bool(..) => continue,
+                ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()),
+                ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default),
+            };
+
+            group.settings.push(Setting {
+                name: s.name,
+                comment: s.comment,
+                byte_offset,
+                specific,
+            });
+
+            byte_offset += 1;
+        }
+
+        group.bool_start_byte_offset = byte_offset;
+
+        // Then the boolean settings.
+        for s in &self.settings {
+            let (default, predicate_number) = match s.specific {
+                ProtoSpecificSetting::Bool(default, predicate_number) => {
+                    (default, predicate_number)
+                }
+                ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
+            };
+            group.settings.push(Setting {
+                name: s.name,
+                comment: s.comment,
+                byte_offset: byte_offset + predicate_number / 8,
+                specific: SpecificSetting::Bool(BoolSetting {
+                    default,
+                    bit_offset: predicate_number % 8,
+                    predicate_number,
+                }),
+            });
+        }
+
+        assert!(
+            group.predicates.len() == 0,
+            "settings_size is the byte size before adding predicates"
+        );
+        group.settings_size = group.byte_size();
+
+        group.predicates.extend(self.predicates);
+        group.presets.extend(self.presets);
+
+        group
+    }
+}
--- a/third_party/rust/cranelift-codegen-meta/src/cdsl/types.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/cdsl/types.rs
@@ -185,29 +185,30 @@ impl LaneType {
             LaneType::BoolType(ref b) => *b as u64,
             LaneType::FloatType(ref f) => *f as u64,
             LaneType::IntType(ref i) => *i as u64,
         }
     }
 
     /// Find the unique number associated with this lane type.
     pub fn number(self) -> u8 {
-        LANE_BASE + match self {
-            LaneType::BoolType(base_types::Bool::B1) => 0,
-            LaneType::BoolType(base_types::Bool::B8) => 1,
-            LaneType::BoolType(base_types::Bool::B16) => 2,
-            LaneType::BoolType(base_types::Bool::B32) => 3,
-            LaneType::BoolType(base_types::Bool::B64) => 4,
-            LaneType::IntType(base_types::Int::I8) => 5,
-            LaneType::IntType(base_types::Int::I16) => 6,
-            LaneType::IntType(base_types::Int::I32) => 7,
-            LaneType::IntType(base_types::Int::I64) => 8,
-            LaneType::FloatType(base_types::Float::F32) => 9,
-            LaneType::FloatType(base_types::Float::F64) => 10,
-        }
+        LANE_BASE
+            + match self {
+                LaneType::BoolType(base_types::Bool::B1) => 0,
+                LaneType::BoolType(base_types::Bool::B8) => 1,
+                LaneType::BoolType(base_types::Bool::B16) => 2,
+                LaneType::BoolType(base_types::Bool::B32) => 3,
+                LaneType::BoolType(base_types::Bool::B64) => 4,
+                LaneType::IntType(base_types::Int::I8) => 5,
+                LaneType::IntType(base_types::Int::I16) => 6,
+                LaneType::IntType(base_types::Int::I32) => 7,
+                LaneType::IntType(base_types::Int::I64) => 8,
+                LaneType::FloatType(base_types::Float::F32) => 9,
+                LaneType::FloatType(base_types::Float::F64) => 10,
+            }
     }
 }
 
 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()),
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/constant_hash.rs
@@ -0,0 +1,50 @@
+pub fn simple_hash(s: &str) -> usize {
+    let mut h: u32 = 5381;
+    for c in s.chars() {
+        h = (h ^ c as u32).wrapping_add(h.rotate_right(6));
+    }
+    h as usize
+}
+
+/// Compute an open addressed, quadratically probed hash table containing
+/// `items`. The returned table is a list containing the elements of the
+/// iterable `items` and `None` in unused slots.
+pub fn generate_table<T, H: Fn(&T) -> usize>(items: &Vec<T>, hash_function: H) -> Vec<Option<&T>> {
+    let size = (1.20 * items.len() as f64) as usize;
+    // TODO do we really need the multiply by two here?
+    let size = if size.is_power_of_two() {
+        size * 2
+    } else {
+        size.next_power_of_two()
+    };
+
+    let mut table: Vec<Option<&T>> = Vec::new();
+    table.resize(size, None);
+
+    for i in items {
+        let mut h = hash_function(i) % size;
+        let mut s = 0;
+        while table[h].is_some() {
+            s += 1;
+            h = (h + s) % size;
+        }
+        table[h] = Some(i);
+    }
+
+    table
+}
+
+#[test]
+fn test_generate_table() {
+    let v = vec!["Hello".to_string(), "world".to_string()];
+    let table = generate_table(&v, |s| simple_hash(&s));
+    assert_eq!(
+        table,
+        vec![
+            None,
+            Some(&"Hello".to_string()),
+            Some(&"world".to_string()),
+            None
+        ]
+    );
+}
--- a/third_party/rust/cranelift-codegen-meta/src/gen_registers.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/gen_registers.rs
@@ -73,17 +73,17 @@ fn gen_regbank_units(reg_bank: &RegBank,
         if (unit as usize) < reg_bank.names.len() {
             fmt.line(&format!("{} = {},", reg_bank.names[unit as usize], v));
             continue;
         }
         fmt.line(&format!("{}{} = {},", reg_bank.prefix, unit, v));
     }
 }
 
-fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) -> Result<(), error::Error> {
+fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) {
     // Emit RegInfo.
     fmt.line("pub static INFO: RegInfo = RegInfo {");
 
     fmt.indent(|fmt| {
         fmt.line("banks: &[");
         // Bank descriptors.
         fmt.indent(|fmt| {
             for reg_bank in isa.reg_banks.values() {
@@ -123,18 +123,16 @@ fn gen_isa(isa: &TargetIsa, fmt: &mut Fo
     fmt.indent(|fmt| {
         fmt.line("fn into(self) -> RegUnit {");
         fmt.indent(|fmt| {
             fmt.line("self as RegUnit");
         });
         fmt.line("}")
     });
     fmt.line("}");
-
-    Ok(())
 }
 
-pub fn generate(isa: TargetIsa, base_filename: &str, out_dir: &str) -> Result<(), error::Error> {
+pub fn generate(isa: &TargetIsa, base_filename: &str, out_dir: &str) -> Result<(), error::Error> {
     let mut fmt = Formatter::new();
-    gen_isa(&isa, &mut fmt)?;
+    gen_isa(&isa, &mut fmt);
     fmt.update_file(&format!("{}-{}.rs", base_filename, isa.name), out_dir)?;
     Ok(())
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/gen_settings.rs
@@ -0,0 +1,447 @@
+use base;
+use cdsl::camel_case;
+use cdsl::isa::TargetIsa;
+use cdsl::settings::{BoolSetting, Predicate, Preset, Setting, SettingGroup, SpecificSetting};
+use constant_hash::{generate_table, simple_hash};
+use error;
+use srcgen::{Formatter, Match};
+use std::collections::HashMap;
+use unique_table::UniqueTable;
+
+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",
+        ParentGroup::Shared => "shared: &settings::Flags, builder: Builder",
+    };
+    fmt.line("impl Flags {");
+    fmt.indent(|fmt| {
+        fmt.doc_comment(&format!("Create flags {} settings group.", group.name));
+        fmt.line("#[allow(unused_variables)]");
+        fmt.line(&format!("pub fn new({}) -> Self {{", args));
+        fmt.indent(|fmt| {
+            fmt.line(&format!(
+                "let bvec = builder.state_for(\"{}\");",
+                group.name
+            ));
+            fmt.line(&format!(
+                "let mut {} = Self {{ bytes: [0; {}] }};",
+                group.name,
+                group.byte_size()
+            ));
+            fmt.line(&format!(
+                "debug_assert_eq!(bvec.len(), {});",
+                group.settings_size
+            ));
+            fmt.line(&format!(
+                "{}.bytes[0..{}].copy_from_slice(&bvec);",
+                group.name, group.settings_size
+            ));
+
+            // Now compute the predicates.
+            for p in &group.predicates {
+                fmt.comment(&format!("Precompute #{}.", p.number));
+                fmt.line(&format!("if {} {{", p.render(group)));
+                fmt.indent(|fmt| {
+                    fmt.line(&format!(
+                        "{}.bytes[{}] |= 1 << {};",
+                        group.name,
+                        group.bool_start_byte_offset + p.number / 8,
+                        p.number % 8
+                    ));
+                });
+                fmt.line("}");
+            }
+
+            fmt.line(group.name);
+        });
+        fmt.line("}");
+    });
+    fmt.line("}");
+}
+
+/// Emit Display and FromStr implementations for enum settings.
+fn gen_to_and_from_str(name: &str, values: &[&'static str], fmt: &mut Formatter) {
+    fmt.line(&format!("impl fmt::Display for {} {{", name));
+    fmt.indent(|fmt| {
+        fmt.line("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {");
+        fmt.indent(|fmt| {
+            fmt.line("f.write_str(match *self {");
+            fmt.indent(|fmt| {
+                for v in values.iter() {
+                    fmt.line(&format!("{}::{} => \"{}\",", name, camel_case(v), v));
+                }
+            });
+            fmt.line("})");
+        });
+        fmt.line("}");
+    });
+    fmt.line("}");
+
+    fmt.line(&format!("impl str::FromStr for {} {{", name));
+    fmt.indent(|fmt| {
+        fmt.line("type Err = ();");
+        fmt.line("fn from_str(s: &str) -> Result<Self, Self::Err> {");
+        fmt.indent(|fmt| {
+            fmt.line("match s {");
+            fmt.indent(|fmt| {
+                for v in values.iter() {
+                    fmt.line(&format!("\"{}\" => Ok({}::{}),", v, name, camel_case(v)));
+                }
+                fmt.line("_ => Err(()),");
+            });
+            fmt.line("}");
+        });
+        fmt.line("}");
+    });
+    fmt.line("}");
+}
+
+/// Emit real enum for the Enum settings.
+fn gen_enum_types(group: &SettingGroup, fmt: &mut Formatter) {
+    for setting in group.settings.iter() {
+        let values = match setting.specific {
+            SpecificSetting::Bool(_) | SpecificSetting::Num(_) => continue,
+            SpecificSetting::Enum(ref values) => values,
+        };
+        let name = camel_case(setting.name);
+
+        fmt.doc_comment(&format!("Values for `{}.{}`.", group.name, setting.name));
+        fmt.line("#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]");
+        fmt.line(&format!("pub enum {} {{", name));
+        fmt.indent(|fmt| {
+            for v in values.iter() {
+                fmt.doc_comment(&format!("`{}`.", v));
+                fmt.line(&format!("{},", camel_case(v)));
+            }
+        });
+        fmt.line("}");
+
+        gen_to_and_from_str(&name, values, fmt);
+    }
+}
+
+/// Emit a getter function for `setting`.
+fn gen_getter(setting: &Setting, fmt: &mut Formatter) {
+    fmt.doc_comment(setting.comment);
+    match setting.specific {
+        SpecificSetting::Bool(BoolSetting {
+            predicate_number, ..
+        }) => {
+            fmt.line(&format!("pub fn {}(&self) -> bool {{", setting.name));
+            fmt.indent(|fmt| {
+                fmt.line(&format!("self.numbered_predicate({})", predicate_number));
+            });
+            fmt.line("}");
+        }
+        SpecificSetting::Enum(ref values) => {
+            let ty = camel_case(setting.name);
+            fmt.line(&format!("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("_", vec![], "panic!(\"Invalid enum value\")");
+                fmt.add_match(m);
+            });
+            fmt.line("}");
+        }
+        SpecificSetting::Num(_) => {
+            fmt.line(&format!("pub fn {}(&self) -> u8 {{", setting.name));
+            fmt.indent(|fmt| {
+                fmt.line(&format!("self.bytes[{}]", setting.byte_offset));
+            });
+            fmt.line("}");
+        }
+    }
+}
+
+fn gen_pred_getter(predicate: &Predicate, group: &SettingGroup, fmt: &mut Formatter) {
+    fmt.doc_comment(&format!(
+        "Computed predicate `{}`.",
+        predicate.render(group)
+    ));
+    fmt.line(&format!("pub fn {}(&self) -> bool {{", predicate.name));
+    fmt.indent(|fmt| {
+        fmt.line(&format!("self.numbered_predicate({})", predicate.number));
+    });
+    fmt.line("}");
+}
+
+/// Emits getters for each setting value.
+fn gen_getters(group: &SettingGroup, fmt: &mut Formatter) {
+    fmt.doc_comment("User-defined settings.");
+    fmt.line("#[allow(dead_code)]");
+    fmt.line("impl Flags {");
+    fmt.indent(|fmt| {
+        fmt.doc_comment("Get a view of the boolean predicates.");
+        fmt.line("pub fn predicate_view(&self) -> ::settings::PredicateView {");
+        fmt.indent(|fmt| {
+            fmt.line(&format!(
+                "::settings::PredicateView::new(&self.bytes[{}..])",
+                group.bool_start_byte_offset
+            ));
+        });
+        fmt.line("}");
+
+        if group.settings.len() > 0 {
+            fmt.doc_comment("Dynamic numbered predicate getter.");
+            fmt.line("fn numbered_predicate(&self, p: usize) -> bool {");
+            fmt.indent(|fmt| {
+                fmt.line(&format!(
+                    "self.bytes[{} + p / 8] & (1 << (p % 8)) != 0",
+                    group.bool_start_byte_offset
+                ));
+            });
+            fmt.line("}");
+        }
+
+        for setting in &group.settings {
+            gen_getter(&setting, fmt);
+        }
+        for predicate in &group.predicates {
+            gen_pred_getter(&predicate, &group, fmt);
+        }
+    });
+    fmt.line("}");
+}
+
+#[derive(Hash, PartialEq, Eq)]
+enum SettingOrPreset<'a> {
+    Setting(&'a Setting),
+    Preset(&'a Preset),
+}
+
+impl<'a> SettingOrPreset<'a> {
+    fn name(&self) -> &str {
+        match self {
+            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 descriptor_index_map: HashMap<SettingOrPreset, usize> = HashMap::new();
+
+    // Generate descriptors.
+    fmt.line(&format!(
+        "static DESCRIPTORS: [detail::Descriptor; {}] = [",
+        group.settings.len() + group.presets.len()
+    ));
+    fmt.indent(|fmt| {
+        for (idx, setting) in group.settings.iter().enumerate() {
+            fmt.line("detail::Descriptor {");
+            fmt.indent(|fmt| {
+                fmt.line(&format!("name: \"{}\",", setting.name));
+                fmt.line(&format!("offset: {},", setting.byte_offset));
+                match &setting.specific {
+                    SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
+                        fmt.line(&format!(
+                            "detail: detail::Detail::Bool {{ bit: {} }},",
+                            bit_offset
+                        ));
+                    }
+                    SpecificSetting::Enum(values) => {
+                        let offset = enum_table.add(values);
+                        fmt.line(&format!(
+                            "detail: detail::Detail::Enum {{ last: {}, enumerators: {} }},",
+                            values.len() - 1,
+                            offset
+                        ));
+                    }
+                    SpecificSetting::Num(_) => {
+                        fmt.line("detail: detail::Detail::Num,");
+                    }
+                }
+
+                descriptor_index_map.insert(SettingOrPreset::Setting(setting), idx);
+            });
+            fmt.line("},");
+        }
+
+        for (idx, preset) in group.presets.iter().enumerate() {
+            fmt.line("detail::Descriptor {");
+            fmt.indent(|fmt| {
+                fmt.line(&format!("name: \"{}\",", preset.name));
+                fmt.line(&format!("offset: {},", (idx as u8) * group.settings_size));
+                fmt.line("detail: detail::Detail::Preset,");
+            });
+            fmt.line("},");
+
+            descriptor_index_map.insert(SettingOrPreset::Preset(preset), idx);
+        }
+    });
+    fmt.line("];");
+
+    // Generate enumerators.
+    fmt.line(&format!(
+        "static ENUMERATORS: [&str; {}] = [",
+        enum_table.len()
+    ));
+    fmt.indent(|fmt| {
+        for enum_val in enum_table.iter() {
+            fmt.line(&format!("\"{}\",", enum_val));
+        }
+    });
+    fmt.line("];");
+
+    // Generate hash table.
+    let mut hash_entries: Vec<SettingOrPreset> = Vec::new();
+    hash_entries.extend(
+        group
+            .settings
+            .iter()
+            .map(|x| SettingOrPreset::Setting(x))
+            .collect::<Vec<SettingOrPreset>>(),
+    );
+    hash_entries.extend(
+        group
+            .presets
+            .iter()
+            .map(|x| SettingOrPreset::Preset(x))
+            .collect::<Vec<SettingOrPreset>>(),
+    );
+    let hash_table = generate_table(&hash_entries, |entry| simple_hash(entry.name()));
+    fmt.line(&format!(
+        "static HASH_TABLE: [u16; {}] = [",
+        hash_table.len()
+    ));
+    fmt.indent(|fmt| {
+        for h in &hash_table {
+            match h {
+                Some(setting_or_preset) => fmt.line(&format!(
+                    "{},",
+                    &descriptor_index_map
+                        .get(setting_or_preset)
+                        .unwrap()
+                        .to_string()
+                )),
+                None => fmt.line("0xffff,"),
+            }
+        }
+    });
+    fmt.line("];");
+
+    // Generate presets.
+    fmt.line(&format!(
+        "static PRESETS: [(u8, u8); {}] = [",
+        group.presets.len()
+    ));
+    fmt.indent(|fmt| {
+        for preset in &group.presets {
+            fmt.comment(preset.name);
+            for (mask, value) in preset.layout(&group) {
+                fmt.line(&format!("(0b{:08b}, 0b{:08b}),", mask, value));
+            }
+        }
+    });
+    fmt.line("];");
+}
+
+fn gen_template(group: &SettingGroup, fmt: &mut Formatter) {
+    let mut default_bytes: Vec<u8> = Vec::new();
+    default_bytes.resize(group.settings_size as usize, 0);
+    for setting in &group.settings {
+        *default_bytes.get_mut(setting.byte_offset as usize).unwrap() |= setting.default_byte();
+    }
+
+    let default_bytes: Vec<String> = default_bytes
+        .iter()
+        .map(|x| format!("{:#04x}", x))
+        .collect();
+    let default_bytes_str = default_bytes.join(", ");
+
+    fmt.line("static TEMPLATE: detail::Template = detail::Template {");
+    fmt.indent(|fmt| {
+        fmt.line(&format!("name: \"{}\",", group.name));
+        fmt.line("descriptors: &DESCRIPTORS,");
+        fmt.line("enumerators: &ENUMERATORS,");
+        fmt.line("hash_table: &HASH_TABLE,");
+        fmt.line(&format!("defaults: &[{}],", default_bytes_str));
+        fmt.line("presets: &PRESETS,");
+    });
+    fmt.line("};");
+
+    fmt.doc_comment(&format!(
+        "Create a `settings::Builder` for the {} settings group.",
+        group.name
+    ));
+    fmt.line("pub fn builder() -> Builder {");
+    fmt.indent(|fmt| {
+        fmt.line("Builder::new(&TEMPLATE)");
+    });
+    fmt.line("}");
+}
+
+fn gen_display(group: &SettingGroup, fmt: &mut Formatter) {
+    fmt.line("impl fmt::Display for Flags {");
+    fmt.indent(|fmt| {
+        fmt.line("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {");
+        fmt.indent(|fmt| {
+            fmt.line(&format!("writeln!(f, \"[{}]\")?;", group.name));
+            fmt.line("for d in &DESCRIPTORS {");
+            fmt.indent(|fmt| {
+                fmt.line("if !d.detail.is_preset() {");
+                fmt.indent(|fmt| {
+                    fmt.line("write!(f, \"{} = \", d.name)?;");
+                    fmt.line(
+                        "TEMPLATE.format_toml_value(d.detail, self.bytes[d.offset as usize], f)?;",
+                    );
+                    fmt.line("writeln!(f)?;");
+                });
+                fmt.line("}");
+            });
+            fmt.line("}");
+            fmt.line("Ok(())");
+        });
+        fmt.line("}")
+    });
+    fmt.line("}");
+}
+
+fn gen_group(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) {
+    // Generate struct.
+    fmt.line("#[derive(Clone)]");
+    fmt.doc_comment(&format!("Flags group `{}`.", group.name));
+    fmt.line("pub struct Flags {");
+    fmt.indent(|fmt| {
+        fmt.line(&format!("bytes: [u8; {}],", group.byte_size()));
+    });
+    fmt.line("}");
+
+    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 = base::settings::generate();
+    let mut fmt = Formatter::new();
+    gen_group(&settings, ParentGroup::None, &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,13 +1,19 @@
 use cdsl::isa::{TargetIsa, TargetIsaBuilder};
 use cdsl::regs::{RegBankBuilder, RegClassBuilder};
+use cdsl::settings::{SettingGroup, SettingGroupBuilder};
 
-pub fn define() -> TargetIsa {
-    let mut isa = TargetIsaBuilder::new("arm32");
+fn define_settings(_shared: &SettingGroup) -> SettingGroup {
+    let setting = SettingGroupBuilder::new("arm32");
+    setting.finish()
+}
+
+pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
+    let mut isa = TargetIsaBuilder::new("arm32", define_settings(shared_settings));
 
     let builder = RegBankBuilder::new("FloatRegs", "s")
         .units(64)
         .track_pressure(true);
     let float_regs = isa.add_reg_bank(builder);
 
     let builder = RegBankBuilder::new("IntRegs", "r")
         .units(16)
--- 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,13 +1,19 @@
 use cdsl::isa::{TargetIsa, TargetIsaBuilder};
 use cdsl::regs::{RegBankBuilder, RegClassBuilder};
+use cdsl::settings::{SettingGroup, SettingGroupBuilder};
 
-pub fn define() -> TargetIsa {
-    let mut isa = TargetIsaBuilder::new("arm64");
+fn define_settings(_shared: &SettingGroup) -> SettingGroup {
+    let setting = SettingGroupBuilder::new("arm64");
+    setting.finish()
+}
+
+pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
+    let mut isa = TargetIsaBuilder::new("arm64", define_settings(shared_settings));
 
     // The `x31` regunit serves as the stack pointer / zero register depending on context. We
     // reserve it and don't model the difference.
     let builder = RegBankBuilder::new("IntRegs", "x")
         .units(32)
         .track_pressure(true);
     let int_regs = isa.add_reg_bank(builder);
 
--- a/third_party/rust/cranelift-codegen-meta/src/isa/mod.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/isa/mod.rs
@@ -1,9 +1,10 @@
 use cdsl::isa::TargetIsa;
+use cdsl::settings::SettingGroup;
 use std::fmt;
 
 mod arm32;
 mod arm64;
 mod riscv;
 mod x86;
 
 /// Represents known ISA target.
@@ -56,16 +57,16 @@ 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_all() -> Vec<TargetIsa> {
+pub fn define_all(shared_settings: &SettingGroup) -> Vec<TargetIsa> {
     vec![
-        riscv::define(),
-        arm32::define(),
-        arm64::define(),
-        x86::define(),
+        riscv::define(shared_settings),
+        arm32::define(shared_settings),
+        arm64::define(shared_settings),
+        x86::define(shared_settings),
     ]
 }
--- 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,13 +1,66 @@
 use cdsl::isa::{TargetIsa, TargetIsaBuilder};
 use cdsl::regs::{RegBankBuilder, RegClassBuilder};
+use cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
 
-pub fn define() -> TargetIsa {
-    let mut isa = TargetIsaBuilder::new("riscv");
+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,
+    );
+    let supports_a = setting.add_bool(
+        "supports_a",
+        "CPU supports the 'A' extension (atomics)",
+        false,
+    );
+    let supports_f = setting.add_bool(
+        "supports_f",
+        "CPU supports the 'F' extension (float)",
+        false,
+    );
+    let supports_d = setting.add_bool(
+        "supports_d",
+        "CPU supports the 'D' extension (double)",
+        false,
+    );
+
+    let enable_m = setting.add_bool(
+        "enable_m",
+        "Enable the use of 'M' instructions if available",
+        true,
+    );
+
+    setting.add_bool(
+        "enable_e",
+        "Enable the 'RV32E' instruction set with only 16 registers",
+        true,
+    );
+
+    let shared_enable_atomics = shared.get_bool("enable_atomics");
+    let shared_enable_float = shared.get_bool("enable_float");
+    let shared_enable_simd = shared.get_bool("enable_simd");
+
+    setting.add_predicate("use_m", predicate!(supports_m && enable_m));
+    setting.add_predicate("use_a", predicate!(supports_a && shared_enable_atomics));
+    setting.add_predicate("use_f", predicate!(supports_f && shared_enable_float));
+    setting.add_predicate("use_d", predicate!(supports_d && shared_enable_float));
+    setting.add_predicate(
+        "full_float",
+        predicate!(shared_enable_simd && supports_f && supports_d),
+    );
+
+    setting.finish()
+}
+
+pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
+    let mut isa = TargetIsaBuilder::new("riscv", define_settings(shared_settings));
 
     let builder = RegBankBuilder::new("IntRegs", "x")
         .units(32)
         .track_pressure(true);
     let int_regs = isa.add_reg_bank(builder);
 
     let builder = RegBankBuilder::new("FloatRegs", "f")
         .units(32)
--- 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,14 +1,79 @@
 use cdsl::isa::{TargetIsa, TargetIsaBuilder};
 use cdsl::regs::{RegBankBuilder, RegClassBuilder};
+use cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
 
-pub fn define() -> TargetIsa {
-    let mut isa = TargetIsaBuilder::new("x86");
+pub fn define_settings(_shared: &SettingGroup) -> SettingGroup {
+    let mut settings = SettingGroupBuilder::new("x86");
+
+    // CPUID.01H:ECX
+    let has_sse3 = settings.add_bool("has_sse3", "SSE3: CPUID.01H:ECX.SSE3[bit 0]", false);
+    let has_ssse3 = settings.add_bool("has_ssse3", "SSSE3: CPUID.01H:ECX.SSSE3[bit 9]", false);
+    let has_sse41 = settings.add_bool("has_sse41", "SSE4.1: CPUID.01H:ECX.SSE4_1[bit 19]", false);
+    let has_sse42 = settings.add_bool("has_sse42", "SSE4.2: CPUID.01H:ECX.SSE4_2[bit 20]", false);
+    let has_popcnt = settings.add_bool("has_popcnt", "POPCNT: CPUID.01H:ECX.POPCNT[bit 23]", false);
+    settings.add_bool("has_avx", "AVX: CPUID.01H:ECX.AVX[bit 28]", false);
+
+    // CPUID.(EAX=07H, ECX=0H):EBX
+    let has_bmi1 = settings.add_bool(
+        "has_bmi1",
+        "BMI1: CPUID.(EAX=07H, ECX=0H):EBX.BMI1[bit 3]",
+        false,
+    );
+    let has_bmi2 = settings.add_bool(
+        "has_bmi2",
+        "BMI2: CPUID.(EAX=07H, ECX=0H):EBX.BMI2[bit 8]",
+        false,
+    );
+
+    // CPUID.EAX=80000001H:ECX
+    let has_lzcnt = settings.add_bool(
+        "has_lzcnt",
+        "LZCNT: CPUID.EAX=80000001H:ECX.LZCNT[bit 5]",
+        false,
+    );
 
+    settings.add_predicate("use_sse41", predicate!(has_sse41));
+    settings.add_predicate("use_sse42", predicate!(has_sse41 && has_sse42));
+    settings.add_predicate("use_popcnt", predicate!(has_popcnt && has_sse42));
+    settings.add_predicate("use_bmi1", predicate!(has_bmi1));
+    settings.add_predicate("use_lznct", predicate!(has_lzcnt));
+
+    settings.add_preset("baseline", preset!());
+    let nehalem = settings.add_preset(
+        "nehalem",
+        preset!(has_sse3 && has_ssse3 && has_sse41 && has_sse42 && has_popcnt),
+    );
+    let haswell = settings.add_preset(
+        "haswell",
+        preset!(nehalem && has_bmi1 && has_bmi2 && has_lzcnt),
+    );
+    let broadwell = settings.add_preset("broadwell", preset!(haswell));
+    let skylake = settings.add_preset("skylake", preset!(broadwell));
+    let cannonlake = settings.add_preset("cannonlake", preset!(skylake));
+    settings.add_preset("icelake", preset!(cannonlake));
+    settings.add_preset(
+        "znver1",
+        preset!(
+            has_sse3
+                && has_ssse3
+                && has_sse41
+                && has_sse42
+                && has_popcnt
+                && has_bmi1
+                && has_bmi2
+                && has_lzcnt
+        ),
+    );
+
+    settings.finish()
+}
+
+fn define_registers(isa: &mut TargetIsaBuilder) {
     let builder = RegBankBuilder::new("IntRegs", "r")
         .units(16)
         .names(vec!["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"])
         .track_pressure(true);
     let int_regs = isa.add_reg_bank(builder);
 
     let builder = RegBankBuilder::new("FloatRegs", "xmm")
         .units(16)
@@ -33,11 +98,19 @@ pub fn define() -> TargetIsa {
     let builder = RegClassBuilder::subclass_of("GPR8", gpr, 0, 8);
     let gpr8 = isa.add_reg_class(builder);
 
     let builder = RegClassBuilder::subclass_of("ABCD", gpr8, 0, 4);
     isa.add_reg_class(builder);
 
     let builder = RegClassBuilder::subclass_of("FPR8", fpr, 0, 8);
     isa.add_reg_class(builder);
+}
+
+pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
+    let settings = define_settings(shared_settings);
+
+    let mut isa = TargetIsaBuilder::new("x86", settings);
+
+    define_registers(&mut isa);
 
     isa.finish()
 }
--- a/third_party/rust/cranelift-codegen-meta/src/lib.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/lib.rs
@@ -1,11 +1,16 @@
 #[macro_use]
 extern crate cranelift_entity;
 
+#[macro_use]
+mod cdsl;
+
 pub mod error;
 pub mod gen_registers;
+pub mod gen_settings;
 pub mod gen_types;
 pub mod isa;
 
 mod base;
-mod cdsl;
+mod constant_hash;
 mod srcgen;
+mod unique_table;
--- a/third_party/rust/cranelift-codegen-meta/src/srcgen.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/srcgen.rs
@@ -1,14 +1,15 @@
 //! Source code generator.
 //!
 //! The `srcgen` module contains generic helper routines and classes for
 //! generating source code.
 
-use std::collections::{BTreeMap, HashSet};
+use std::cmp;
+use std::collections::{BTreeMap, BTreeSet};
 use std::fs;
 use std::io::Write;
 use std::path;
 
 use error;
 
 static SHIFTWIDTH: usize = 4;
 
@@ -93,32 +94,60 @@ impl Formatter {
     }
 
     /// Add one or more lines after stripping common indentation.
     pub fn _multi_line(&mut self, s: &str) {
         parse_multiline(s).into_iter().for_each(|l| self.line(&l));
     }
 
     /// Add a comment line.
-    pub fn _comment(&mut self, s: &str) {
+    pub fn comment(&mut self, s: &str) {
         let commented_line = format!("// {}", s);
         self.line(&commented_line);
     }
 
     /// Add a (multi-line) documentation comment.
     pub fn doc_comment(&mut self, contents: &str) {
         parse_multiline(contents)
             .iter()
-            .map(|l| format!("/// {}", l))
+            .map(|l| {
+                if l.len() == 0 {
+                    "///".into()
+                } else {
+                    format!("/// {}", l)
+                }
+            })
             .for_each(|s| self.line(s.as_str()));
     }
 
     /// Add a match expression.
-    fn _add_match(&mut self, _m: &_Match) {
-        unimplemented!();
+    pub fn add_match(&mut self, m: Match) {
+        self.line(&format!("match {} {{", m.expr));
+        self.indent(|fmt| {
+            for ((fields, body), names) in m.arms.iter() {
+                // name { fields } | name { fields } => { body }
+                let conditions: Vec<String> = names
+                    .iter()
+                    .map(|name| {
+                        if fields.len() > 0 {
+                            format!("{} {{ {} }}", name, fields.join(", "))
+                        } else {
+                            name.clone()
+                        }
+                    })
+                    .collect();
+                let lhs = conditions.join(" | ");
+                fmt.line(&format!("{} => {{", lhs));
+                fmt.indent(|fmt| {
+                    fmt.line(body);
+                });
+                fmt.line("}");
+            }
+        });
+        self.line("}");
     }
 }
 
 /// Compute the indentation of s, or None of an empty line.
 fn _indent(s: &str) -> Option<usize> {
     if s.is_empty() {
         None
     } else {
@@ -130,36 +159,38 @@ fn _indent(s: &str) -> Option<usize> {
 /// Given a multi-line string, split it into a sequence of lines after
 /// stripping a common indentation. This is useful for strings defined with
 /// doc strings.
 fn parse_multiline(s: &str) -> Vec<String> {
     // Convert tabs into spaces.
     let expanded_tab = format!("{:-1$}", " ", SHIFTWIDTH);
     let lines: Vec<String> = s.lines().map(|l| l.replace("\t", &expanded_tab)).collect();
 
-    // Determine minimum indentation, ignoring the first line.
+    // Determine minimum indentation, ignoring the first line and empty lines.
     let indent = lines
         .iter()
         .skip(1)
+        .filter(|l| !l.trim().is_empty())
         .map(|l| l.len() - l.trim_left().len())
         .min();
 
     // Strip off leading blank lines.
     let mut lines_iter = lines.iter().skip_while(|l| l.is_empty());
     let mut trimmed = Vec::with_capacity(lines.len());
 
     // Remove indentation (first line is special)
     if let Some(s) = lines_iter.next().map(|l| l.trim()).map(|l| l.to_string()) {
         trimmed.push(s);
     }
 
     // Remove trailing whitespace from other lines.
     let mut other_lines = if let Some(indent) = indent {
+        // Note that empty lines may have fewer than `indent` chars.
         lines_iter
-            .map(|l| &l[indent..])
+            .map(|l| &l[cmp::min(indent, l.len())..])
             .map(|l| l.trim_right())
             .map(|l| l.to_string())
             .collect::<Vec<_>>()
     } else {
         lines_iter
             .map(|l| l.trim_right())
             .map(|l| l.to_string())
             .collect::<Vec<_>>()
@@ -181,70 +212,104 @@ fn parse_multiline(s: &str) -> Vec<Strin
 }
 
 /// Match formatting class.
 ///
 /// Match objects collect all the information needed to emit a Rust `match`
 /// expression, automatically deduplicating overlapping identical arms.
 ///
 /// Note that this class is ignorant of Rust types, and considers two fields
-/// with the same name to be equivalent. A BTreeMap is used to represent the
-/// arms in order to make the order deterministic.
-struct _Match<'a> {
-    _expr: &'a str,
-    arms: BTreeMap<(Vec<&'a str>, &'a str), HashSet<&'a str>>,
+/// with the same name to be equivalent. BTreeMap/BTreeSet are used to
+/// represent the arms in order to make the order deterministic.
+pub struct Match {
+    expr: String,
+    arms: BTreeMap<(Vec<String>, String), BTreeSet<String>>,
 }
 
-impl<'a> _Match<'a> {
+impl Match {
     /// Create a new match statement on `expr`.
-    fn _new(expr: &'a str) -> Self {
+    pub fn new<T: Into<String>>(expr: T) -> Self {
         Self {
-            _expr: expr,
+            expr: expr.into(),
             arms: BTreeMap::new(),
         }
     }
 
     /// Add an arm to the Match statement.
-    fn _arm(&mut self, name: &'a str, fields: Vec<&'a str>, body: &'a str) {
+    pub fn arm<T: Into<String>>(&mut self, name: T, fields: Vec<T>, body: T) {
         // let key = (fields, body);
-        let match_arm = self.arms.entry((fields, body)).or_insert_with(HashSet::new);
-        match_arm.insert(name);
+        let body = body.into();
+        let fields = fields.into_iter().map(|x| x.into()).collect();
+        let match_arm = self
+            .arms
+            .entry((fields, body))
+            .or_insert_with(BTreeSet::new);
+        match_arm.insert(name.into());
     }
 }
 
 #[cfg(test)]
 mod srcgen_tests {
-    use super::_Match;
     use super::parse_multiline;
     use super::Formatter;
+    use super::Match;
+
+    fn from_raw_string(s: impl Into<String>) -> Vec<String> {
+        s.into()
+            .trim()
+            .split("\n")
+            .into_iter()
+            .map(|x| format!("{}\n", x))
+            .collect()
+    }
 
     #[test]
     fn adding_arms_works() {
-        let mut m = _Match::_new("x");
-        m._arm("Orange", vec!["a", "b"], "some body");
-        m._arm("Yellow", vec!["a", "b"], "some body");
-        m._arm("Green", vec!["a", "b"], "different body");
-        m._arm("Blue", vec!["x", "y"], "some body");
+        let mut m = Match::new("x");
+        m.arm("Orange", vec!["a", "b"], "some body");
+        m.arm("Yellow", vec!["a", "b"], "some body");
+        m.arm("Green", vec!["a", "b"], "different body");
+        m.arm("Blue", vec!["x", "y"], "some body");
         assert_eq!(m.arms.len(), 3);
+
+        let mut fmt = Formatter::new();
+        fmt.add_match(m);
+
+        let expected_lines = from_raw_string(
+            r#"
+match x {
+    Green { a, b } => {
+        different body
+    }
+    Orange { a, b } | Yellow { a, b } => {
+        some body
+    }
+    Blue { x, y } => {
+        some body
+    }
+}
+        "#,
+        );
+        assert_eq!(fmt.lines, expected_lines);
     }
 
     #[test]
     fn parse_multiline_works() {
         let input = "\n    hello\n    world\n";
         let expected = vec!["hello", "world"];
         let output = parse_multiline(input);
         assert_eq!(output, expected);
     }
 
     #[test]
     fn formatter_basic_example_works() {
         let mut fmt = Formatter::new();
         fmt.line("Hello line 1");
         fmt.indent_push();
-        fmt._comment("Nested comment");
+        fmt.comment("Nested comment");
         fmt.indent_pop();
         fmt.line("Back home again");
         let expected_lines = vec![
             "Hello line 1\n",
             "    // Nested comment\n",
             "Back home again\n",
         ];
         assert_eq!(fmt.lines, expected_lines);
@@ -289,9 +354,29 @@ mod srcgen_tests {
 
     #[test]
     fn fmt_can_add_doc_comments() {
         let mut fmt = Formatter::new();
         fmt.doc_comment("documentation\nis\ngood");
         let expected_lines = vec!["/// documentation\n", "/// is\n", "/// good\n"];
         assert_eq!(fmt.lines, expected_lines);
     }
+
+    #[test]
+    fn fmt_can_add_doc_comments_with_empty_lines() {
+        let mut fmt = Formatter::new();
+        fmt.doc_comment(
+            r#"documentation
+        can be really good.
+
+        If you stick to writing it.
+"#,
+        );
+        let expected_lines = from_raw_string(
+            r#"
+/// documentation
+/// can be really good.
+///
+/// If you stick to writing it."#,
+        );
+        assert_eq!(fmt.lines, expected_lines);
+    }
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/unique_table.rs
@@ -0,0 +1,68 @@
+use std::slice;
+
+/// A table of sequences which tries to avoid common subsequences.
+pub struct UniqueTable<T: PartialEq + Clone> {
+    table: Vec<T>,
+}
+
+impl<T: PartialEq + Clone> UniqueTable<T> {
+    pub fn new() -> Self {
+        Self { table: Vec::new() }
+    }
+    pub fn add(&mut self, values: &Vec<T>) -> usize {
+        if let Some(offset) = find_subsequence(values, &self.table) {
+            offset
+        } else {
+            let offset = self.table.len();
+            self.table.extend((*values).clone());
+            offset
+        }
+    }
+    pub fn len(&self) -> usize {
+        self.table.len()
+    }
+    pub fn iter(&self) -> slice::Iter<T> {
+        self.table.iter()
+    }
+}
+
+/// Try to find the subsequence `sub` in the `whole` sequence. Returns None if
+/// it's not been found, or Some(index) if it has been. Naive implementation
+/// until proven we need something better.
+fn find_subsequence<T: PartialEq>(sub: &Vec<T>, whole: &Vec<T>) -> Option<usize> {
+    assert!(sub.len() > 0);
+    // We want i + sub.len() <= whole.len(), i.e. i < whole.len() + 1 - sub.len().
+    if whole.len() < sub.len() {
+        return None;
+    }
+    let max = whole.len() + 1 - sub.len();
+    for i in 0..max {
+        let mut found: Option<usize> = Some(i);
+        for j in 0..sub.len() {
+            if sub[j] != whole[i + j] {
+                found = None;
+                break;
+            }
+        }
+        if found.is_some() {
+            return found;
+        }
+    }
+    return None;
+}
+
+#[test]
+fn test_find_subsequence() {
+    assert_eq!(find_subsequence(&vec![1], &vec![4]), None);
+    assert_eq!(find_subsequence(&vec![1], &vec![1]), Some(0));
+    assert_eq!(find_subsequence(&vec![1, 2], &vec![1]), None);
+    assert_eq!(find_subsequence(&vec![1, 2], &vec![1, 2]), Some(0));
+    assert_eq!(find_subsequence(&vec![1, 2], &vec![1, 3]), None);
+    assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 2]), Some(1));
+    assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 3, 1]), None);
+    assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 3, 1, 2]), Some(3));
+    assert_eq!(
+        find_subsequence(&vec![1, 1, 3], &vec![1, 1, 1, 3, 3]),
+        Some(1)
+    );
+}
--- a/third_party/rust/cranelift-codegen/.cargo-checksum.json
+++ b/third_party/rust/cranelift-codegen/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"d6b246e5928dba1141afc7402f2ac29dfd6e467640efc77282e5cb9080f1bf99","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"e5127227a7db4a8aa92fa6613ed71801025790e696bb41b0323fb7f3c6f7495a","build.rs":"dc816beb002843cd9fcdb47dae9a0a8e3612882411f18bed34d7fda1aa108d90","meta-python/base/__init__.py":"4fac8bb055541dc964383bb04223bae6dfbfe348abf5e23f83655966cbb4aa8f","meta-python/base/entities.py":"0e146dd56dfb93cac88f9557a3501c9804cff584c2723db27435842bb5e2a1b7","meta-python/base/formats.py":"f9fb41210bc1f99a78cb7a60ee9f01c603412c6b1db7e69abbcc0f573cf9fb40","meta-python/base/immediates.py":"f42682d86bda7b569ec2fc2debd9036355999e61caaa9fbf8307e0be8a164814","meta-python/base/instructions.py":"4c3f8ee6c29e8cc9e19172f707890d613ed4173bda9b9de7857dae2b28babcbd","meta-python/base/legalize.py":"57b71ec3599fbf998c1926e4b48566661b7feec01dbd8bd177d0d2aaa26a44c2","meta-python/base/predicates.py":"53a5a9a37021a9762f0faec9f9e3c5b713411d47dd9192423cfe0f7a61916809","meta-python/base/semantics.py":"b90cbca9a143676ac37496c83634a66a83360248e4505ff7bda12b0d454efd92","meta-python/base/settings.py":"97fca9ddaab347f50f1594c93283f8f474e53a7232eae65949f56a6a702c2bba","meta-python/base/types.py":"9616d6fe4cab050827ab02eeb9843eacebbb8f7555521f504b5ee2ddf7214fdb","meta-python/build.py":"b72a80a54e09366878e92dca3a1508af394bf71a0c4b07a05e54058901373d34","meta-python/cdsl/__init__.py":"b531693b8228553ca8bd07e1dcd1aa5855f1ad3741b431d758fc27fdd162b024","meta-python/cdsl/ast.py":"b7c09f69b28b5754f494912ca5b77722dbd4ee416d5fad79cb48322eb544ea9f","meta-python/cdsl/formats.py":"fedfeaec754b40d6a9cc92b827976782c615d8eab1c7f47f6b47510cbef585f4","meta-python/cdsl/instructions.py":"b0ddfd8fd22889bd1e610c103d47087c9c6ae5882862ed44921dafc2ae0463a0","meta-python/cdsl/isa.py":"dc530a4dd5642e3379398dfc8a90ad6ae1692302de63851370bdfa8abf4cdce0","meta-python/cdsl/operands.py":"e24914eae4059b88781bf5a5d7a14ecf98b10a701ed6cf6e88d15981b2ccbfdf","meta-python/cdsl/predicates.py":"def3f91712744671df9cf3d4f9c255a2061f341198689e31addf7db4efb63e36","meta-python/cdsl/registers.py":"939dafd1b8976a6cd456c9a5e4b374af81fafb9da979ea3a1f6d14e4645914a6","meta-python/cdsl/settings.py":"18dc27dd98a82c814c6aeb2686b40d1fed23661bef5e8b4cbf0fb4e7d39f4755","meta-python/cdsl/test_ast.py":"947e934e2862445a158bf266dff58a8c88aae31fb34a7f823309ee58a15c5393","meta-python/cdsl/test_package.py":"ffa53d20e023ecb89137294bb13614f4de9b09e1bf05d9772131570bf78f7987","meta-python/cdsl/test_ti.py":"57966c9eb027fa73bbc7ad81094ff55232053fbb2e16d78849ae3b6e9b1c2989","meta-python/cdsl/test_typevar.py":"714b2d564af1a843629279501436d681cd6590d1988b59e9f50ec940506538bb","meta-python/cdsl/test_xform.py":"ddb6633c7941bbf68570701cb887a81d6b4b27f4bc45eabccf2ce287ad9b77e9","meta-python/cdsl/ti.py":"a1a7ff79f8a2196aee491a3aafbd4f1b231004bbb5281992fc8f4e43f4fb951f","meta-python/cdsl/types.py":"adee4bbc1a9478288fa4b53ee1edeb5ee3296dba2c70bfbe01e923895064999e","meta-python/cdsl/typevar.py":"b5669934eddaf5b9cc0c27b966e2566b5f669f1c5a345f005960930fb499097e","meta-python/cdsl/xform.py":"5cdad80f12d50aa9491cd3d39797e6c0444936bb0874199d7c34e6d735658b34","meta-python/check.sh":"9e2f70f2d762c840f1d49519b024b4c1b93168137184b5e3e605e55813c62ea5","meta-python/constant_hash.py":"c752e6dadf3a9a5bd00c978e85ab27a20c49138a1ccdc6fc9a1904797a4bfe48","meta-python/gen_binemit.py":"76472fb199a330b934ba9ad0a1bbacfb52f0eae7c9a66d83f0d7890970323a2d","meta-python/gen_build_deps.py":"3920c5c89451c26102f7d87c61de64c94e915a545bc8a35d2e49106aecf019ec","meta-python/gen_encoding.py":"0b57d9d74a72a1b3b11721166fdbaa8b8c4b2d7493fc10b88736ac330b83256f","meta-python/gen_instr.py":"7ccd6a6bb1ce1800ea9c2c37e462ac7ded4f908e311d708080f7d21d92578316","meta-python/gen_legalizer.py":"187a47f1702e07487fb8a13076aadcf9c2de3e801d367424dc847ff7b0ed70f1","meta-python/gen_settings.py":"44c231ab8d2aa4f18cbe4fb5a33fb72103503d58f5af22c7b545eeffbf97da79","meta-python/isa/__init__.py":"e499c1206cd095a926fa0ca7eb9d0a50a802ed71c8eb7598e5d3a0f939c8ada5","meta-python/isa/arm32/__init__.py":"eecba73231aa398ded7304690bdba3450dc163afd4360f1b0ad02a28e2380363","meta-python/isa/arm32/defs.py":"01c41dbd7406c624e26229df6befa0992194bddcc7d11e8f6174abfe2b33bf61","meta-python/isa/arm32/registers.py":"c03ca6435828ad5f262049e42f1f71bcf74903831f85daa92c3f322a6c1050ea","meta-python/isa/arm32/settings.py":"afd5a04a9d029f578d6f62dc7c539191886cc9f9dea15d65fc66bf37a63b8814","meta-python/isa/arm64/__init__.py":"f6877253cf786d7ee972881e7d9b3c78c11e6b024e4e227487340dd01d0c44e4","meta-python/isa/arm64/defs.py":"797c5bb6d11fc7a44afe67476136dbd11c40f5e13a1c8f52f9f96be4441677b2","meta-python/isa/arm64/registers.py":"9bdd06edaa382be96042e1ac36d63137e73292292b61dcf4becb7d1428130317","meta-python/isa/arm64/settings.py":"f7b1f8733e775ea8005372ee35f1c2a627b3a69d722e837295599e4cf1f5eb43","meta-python/isa/riscv/__init__.py":"c11607c9eef0bc2707daa3edd4174e934c7a0dcc8ce90cee2c9292a85b1ac596","meta-python/isa/riscv/defs.py":"e73740055c4fb123c45453fc149a807e9720466de848022d5375049bdcfc311c","meta-python/isa/riscv/encodings.py":"ecaad5ea98273ade1cb10606354e893342c495bb48771df50121f789566d7be6","meta-python/isa/riscv/recipes.py":"3852e5b7aa6995fa721ba91744a0470343ce1834651e7b9cc97b5d69af7dfdc5","meta-python/isa/riscv/registers.py":"ef9aca3a6ec2b08ee8f5952186d232861b64a919b671b41911a365e7672b01bd","meta-python/isa/riscv/settings.py":"dfe29722d67be0620a70e08cfb802829a26f5fd339a9342a8ac2dd419daf8a85","meta-python/isa/x86/__init__.py":"ad579de68ea7bf5dc2bce0e3a6f09e7978b1697f1afec8a5ce5dc591136e590d","meta-python/isa/x86/defs.py":"b5eb7889b6f5e5b221ed3923d0137bbb1566c55b5961448cc39e4ea2f13cf4b7","meta-python/isa/x86/encodings.py":"dc758c5bf95b9271e70203c481df0dc9b3363c4f730cfc564e4e3f5f275a6433","meta-python/isa/x86/instructions.py":"530cde78e6b9f6e4ea2192985f4c5c77a987cdc19001d50fb47fa8e36a62f52e","meta-python/isa/x86/legalize.py":"1375ded072c29459e7c0e40ecb02f28d5395d9d8c603eb70e338b2bf2991c9cd","meta-python/isa/x86/recipes.py":"ca56edb2bb87389e2db95d0f0e0fdbf9b27242192de764d60971828bb9a02e60","meta-python/isa/x86/registers.py":"ff934491d07ec6b51fbfd454b865a7c7c191ffbd31b1804615735266b120f4b2","meta-python/isa/x86/settings.py":"d779a768475cf00c2a8d3ddb5cd0a70ce34662e0ebb52ee26a7e1a495ec41aa2","meta-python/mypy.ini":"5ec2f7cc0bbc4fd0435643d6b72e715bd9568a3a0fe14c043f9e559c405b66fb","meta-python/semantics/__init__.py":"e8a25a111f2d9cc9fc7aa498a572a86403d31fe50a7ba59dd2e2560a17804e92","meta-python/semantics/elaborate.py":"3a3fbba83a6818c2d1ce236fd0413111380875a0307f7a5f4b5dd66d8ef714b1","meta-python/semantics/macros.py":"b218c52e1bd4f019dc14a27d315b4f3405a10e5bdc6f2523fe709c8faf91b418","meta-python/semantics/primitives.py":"4e5eb0c90fcc295686732c8c66ad7a793997645c9a676c97babf06823fd2b60d","meta-python/semantics/smtlib.py":"825edfbb9221bf59c02fea26e55d17cf32194da7a9f56ed0e035c44353481055","meta-python/semantics/test_elaborate.py":"3a4c850a7385007422c7549661b211903cd1dd1606dad7a86262ae27e697bca6","meta-python/srcgen.py":"999557d683e808a2ca90688c489ec4aff65798f44ac321ecf7de34d307261913","meta-python/stubs/z3/__init__.pyi":"6aaeb80f783b29c4364dee21da45f6df041c0a4807189a15777ee5447f6515dc","meta-python/stubs/z3/z3core.pyi":"c01a41d468e07cc4f8b405c292ed7f8c82bc1077f8b82dfde1e474577ade3335","meta-python/stubs/z3/z3types.pyi":"30009c951af99b9028d47cd4cabae95ff9742b77b690bd8dd63f6b7dba580759","meta-python/test_constant_hash.py":"157cf4f8964e0f04c041ffd349e889ce565b144453436690578c5d03c3a60216","meta-python/test_gen_legalizer.py":"f16edce7cb2ce53e55b1fc56b6f5ba6a0fc61b291ee4513ec859e36d69f0e285","meta-python/test_srcgen.py":"d6d7775e19a5b2621360c00eb6d92dfcb4568e49220993e0ceaac9628dbfd661","meta-python/unique_table.py":"5bd500667430c15f6ae586603d8612fb3bda07b072e40d86286e08392bdc3127","src/abi.rs":"29f505fdfcb6ec14e561bb408b99ab30ab69b96d1e283a1dcd55db9aff02dbe4","src/binemit/memorysink.rs":"8437e5f5c1b3e68b8e2d0de6fb3a4982f7a06390a0f8820b6379492408df8964","src/binemit/mod.rs":"2f95ea5f6ee20b8d56bdedcacdd41a609c5b999c02affca341d6a71eb59bc113","src/binemit/relaxation.rs":"5facfa8e15a26cba42210102a6fd735df1ba291adf512dabc85c0951239291f8","src/binemit/shrink.rs":"428679a02e44a7b3621a43c7d2e8d2f3b2cb50a36179a4d9862205c0ba34ae50","src/bitset.rs":"67fd02dd2acb9af73e45426e5cab1d2a74d568a11d11ee17ae6a4421f96cf741","src/cfg_printer.rs":"8c0fda88060c204985a6406ba46d7f0a69be96bb18c9fcfc70055d50986068ab","src/constant_hash.rs":"493cdf8f607eab01bb52f93d616fb6190c7c86d3a02e530b0ced30993d4bfda0","src/context.rs":"a44197bd5e05599b643c420d87ca4ad218a8c3ab2fb7b88655b2479014b117b2","src/cursor.rs":"523899307d471f63e9ca35ee4b3340cf55041561a05b199e11cc60f6ad2714f4","src/dbg.rs":"bae915e1f7544f725817b368aed00a6aaa9e394a454dc847d65ad44e54d78cb9","src/dce.rs":"6d015824ae0325f01bb523606d7e15dd5feeb72a75b947f40385feeba94555c8","src/divconst_magic_numbers.rs":"eac50e2353e17ab6f7d1da3bd8e0e8dc9e3122a160efba594d407eb6a8936cc7","src/dominator_tree.rs":"73032e0e7a0ab694aa3181222bccb40630bd6fbda33885a391fd662f7de7d540","src/flowgraph.rs":"fccfade2b24038e18d469e273976f418757159fde140642b5faeb3c664a756ce","src/fx.rs":"2fb53f141b3e6be1882a1e4afac4bc607ce694f045d9329ee823e0aca415898e","src/ir/builder.rs":"19aa7cef76b7577bdd9d769fb1260080a959f9bfdbac569fb1e38307a721a03c","src/ir/condcodes.rs":"5456a25798516245b0923c9e12ae0af5cc98c097fc6f3bc0bf2d7af5008667f7","src/ir/dfg.rs":"cf44ceda665e32d5bd4563d90094fdf519937adbd29096c7291065a2ebdcfbfe","src/ir/entities.rs":"1505347c2baa256b04f69384d1391f52e007d533cb4319d3826cf3046ec1df27","src/ir/extfunc.rs":"9a3535730a39a6b71ca9f1ed679f588e6c3fa48ee7a50489d90803f3134db4a6","src/ir/extname.rs":"5b50dddd0801dfe736433191502e405c879221a6e362be0e2919176b90b4d74c","src/ir/function.rs":"55feb0b0a2bf4a0d194a4d11fc297616c78d5edfa41504795742fd25e7af1399","src/ir/globalvalue.rs":"bf9b76430f6ba564d4e5db85dbebfddf952678be6914326a5549b569c064d03d","src/ir/heap.rs":"dc9d4f0eade20d58b3e2678c329452fbd5965c46d6121cbf5889f2588ae88772","src/ir/immediates.rs":"5f57bc2a46b7ca11e1e495e657cedbf493194025eceb6591ba8792aff6910f88","src/ir/instructions.rs":"2dd2ad70947122e791f41f6cbc7879fd201d4f96d19c138b56559428ba655dab","src/ir/jumptable.rs":"3fc108096e5404c4c30220cfaa4e359caa83bad747268bea88bbc0ac4b1bcd13","src/ir/layout.rs":"77210d256e39d339736ced5518c8d666da6359f656b972d6b6b19581ccaec329","src/ir/libcall.rs":"b35c8d0c90e686a176f9bd157ef6ab3819e8a0b974d0d24007a910ffb0d15f51","src/ir/memflags.rs":"5819e1171c620f171020bf26df9513eb6bee9890da8a7ebabbd1506b1d123f91","src/ir/mod.rs":"16566fb92fc16498d5a2699fa76dfdbc39665a4e8bae3040c03e4f3d2f07e3cb","src/ir/progpoint.rs":"d5191447f82bb612ae25ebceb5ecc2099a902be5aaecd5b9d418dcbd8a2747a5","src/ir/sourceloc.rs":"79eb71609b844839994fc735cd72edf53179eb659303a4150634928e207cee4f","src/ir/stackslot.rs":"d5d0c61555bf7060c58603047356b7db53b5b296497daed8eac356b9e724902d","src/ir/table.rs":"810e92631257e1e54577563df1da9709134438d0eab3db5e540500e69437a16b","src/ir/trapcode.rs":"233d73e4a2abbfc767aac7868d4adbb24cedaf8e7e42a640d590bda2e776784a","src/ir/types.rs":"1f93f886dba75f6bc267b35e2fc6ed1564074a8885af13d6c85c28574acf8436","src/ir/valueloc.rs":"5055897d9acba6d9c396b126889f9b2c7ff3f54a27c1ec5fe70d1a059d633b36","src/isa/arm32/abi.rs":"50ca3161a0f11ba1c2d5b0ff7523d332503cb6a6182695246e4284a486e18cab","src/isa/arm32/binemit.rs":"3197df7b15c223f3a9f2708311375ff423bb247381cf21e26da079f7933f5279","src/isa/arm32/enc_tables.rs":"dacb50bdacfa2085a6283818563e19874590d223e5decb5c91b968e0348e2bf7","src/isa/arm32/mod.rs":"798049325ca439444a5508f63ebb53bf2e0ab16b128c43417017bbdb2fd95742","src/isa/arm32/registers.rs":"0e5c32a218d685c6d37fb46788baedf6bede6be7d4f715808c13620636dfc472","src/isa/arm32/settings.rs":"145f59227c6087e7872f66a6d2183c66e061c40466e7b427a733136e41e41207","src/isa/arm64/abi.rs":"bfd0065a6c26eb407872959c9d7f64169591afa4d69816370e0900aa1ad4e46f","src/isa/arm64/binemit.rs":"159ab9eca03ac4aa7f55d51ab20585a5971d95a74c61953d1899ac863b06e2ec","src/isa/arm64/enc_tables.rs":"00ec8f53402f6cb73716db1adb3aca564574833cab58dc12b4dc6ba8c4529a73","src/isa/arm64/mod.rs":"4eef5f904f2219262c59dae7056af5adb7764074309dffc0be5b2357781dd1a6","src/isa/arm64/registers.rs":"7a8b207ed28737efc737e8e65ce79e7e7d3eecd23599890230dca8474c985026","src/isa/arm64/settings.rs":"7b466fcc2c58bc1846a72f08ec58900b3cf622ab2922ee9128e6dfe254907bf5","src/isa/call_conv.rs":"4bc8f8cc540ed54cf1e4c4688a1a8a975ed49a039a48d44a2c6b97f847b65ea8","src/isa/constraints.rs":"324d7c8655565f13b90a6c9587c9fb6c545ab3086201f434e1324fc5d6e5b3c7","src/isa/enc_tables.rs":"946a9fcdf7d0541e7e65aeafdf18b6a5cb9e3ea29a1ce3bf05c155f910acca3a","src/isa/encoding.rs":"032347d4e624449af7a35f7ba012339bc49fabae97b971818e922d6ca4510e59","src/isa/mod.rs":"a7f966ec68dd709f685157812c7cb40ed012b175666509b9d05fec57b60fdb37","src/isa/registers.rs":"c0014dc940e8a6da628c60e49f6261cebaee1e06e0ea1c172de5f797e600535c","src/isa/riscv/abi.rs":"1de6d0070126e0127ca6a63551a14d1b5c030cf9628538fd732301fd2bd34b5e","src/isa/riscv/binemit.rs":"3bdad2791447f51bfe5ecedb73b1aed8a6a8809790b1f26e3ff624a89163a026","src/isa/riscv/enc_tables.rs":"6bc179f95ef5634b64fb42ab67929c0aeb70ac8efccfc47dd8c9b1dbfe64a446","src/isa/riscv/mod.rs":"de0dd32005f6b0510e3c8a31bb7e58ab7e7cffb674169a08558794950db87126","src/isa/riscv/registers.rs":"794ac61343a8db66dc99b2ca964ea3986b47b37fe8b72575c644c1cdaabd2991","src/isa/riscv/settings.rs":"78ced69b82d89f83b5612a91d0acdac2616f9091b380ee3e4715adb31f95b232","src/isa/stack.rs":"ec96130c446cd7d637722f1603e38d5164498368b652f4b0454baf3227385ad4","src/isa/x86/abi.rs":"6a9f8a3c9ee0f09118be1f962c473f8f5269cc4eba25519ca84df25b8835cceb","src/isa/x86/binemit.rs":"f90820a1f9c4ad02f3507625d35f014f644d8cea93b0216bcc60cc05cc84eacc","src/isa/x86/enc_tables.rs":"aa4bd2992d08ad97e94aafa022cafd853e0978c7c139558885715f57bfcf49e7","src/isa/x86/mod.rs":"bc83c0e619e8603885359c472b4113bc83b8867dd2d8e83b27f798301c6df93c","src/isa/x86/registers.rs":"783ebbe4c3b4711fe1eac2cfd3bbea7c31a53c15c6028397c038ef4eb0b2aa06","src/isa/x86/settings.rs":"dcce098045c1115cd55f256dcd3e0ccaa5a0c9ad93b14fb8a4c680e95caf2b73","src/iterators.rs":"f85f52d3fa707a0eb974c92215b3e976923ce8f9481219f7812e0f2869c2bd37","src/legalizer/boundary.rs":"e86400b42ef60d42e05ed7d2c24dc815f16652b49ab0ee9048edadd460bd411d","src/legalizer/call.rs":"12f380d126765f0bc2da1cf298088fa98451e2d6bf56c34b755ff7077d14a4f1","src/legalizer/globalvalue.rs":"be6fc6e310dedde48e172453eccaa7cb416baa92e1adfc55f565d51159c930cc","src/legalizer/heap.rs":"c44a0845254187dfe7456b0b9efbfea25ae566772bc64b41ed93dd32a71f8e8a","src/legalizer/libcall.rs":"bf18267f502bf9bfbc3ff51bc05df480293a84422ea7f4c37dd0c75e97952766","src/legalizer/mod.rs":"f516c4e22fb66099678f753121dfa92128f2c4524ea4196e1b400e06e6649d44","src/legalizer/split.rs":"6869c2cc436215ed22abffa85c783ae205374eb74f6ec0fc0ddec2b983e799fe","src/legalizer/table.rs":"2aca27ea564c2ef02ed833c495156b9e1ddcee3c8a1b1e70347ab5907a381605","src/lib.rs":"1542f5cae0d80c408f190d225890da959e0b4033687b6c2480e83254341cba83","src/licm.rs":"dcdf4744a01489939955dd02d43be3522cfbc448d92b30796d512b10e784c503","src/loop_analysis.rs":"ab74f702649ddd16d3d91400c3c2aafed4f09d9af210f5e180dff15a82caf8ac","src/nan_canonicalization.rs":"e2b1f9935d14af9abe553de1eda5483cffdaa7098dd1a17ba9a98fa04280b22c","src/partition_slice.rs":"55b9e857f452baf4d6312779861f9ef93b3d5300467e752230ec6af14943efe3","src/postopt.rs":"f1fe06398c644cad19fd427323c74acdb237b5853ffb72148a2b2012b906e2a3","src/predicates.rs":"8e4c4afde65420d33621aedd80e4ce270d334c1b62a17d7879273a1719d49b41","src/print_errors.rs":"54282bd8480b0bab29cbf3c257cdb3faafb566f0c40f88e1ff6c41e742f99ff6","src/ref_slice.rs":"9be914b705eefcc6b4e38db573f56ad7b978f4b65c2f6f8f5516df78fb2206a4","src/regalloc/affinity.rs":"47e28f9ae7cebfc9b1006480143a3a5ab1b118ec761a163c4a670b63fdc47d0a","src/regalloc/coalescing.rs":"5ffab5529e5840e0dc3f13930788caf75ae9e15ce90d4f64f9f625b2ee495605","src/regalloc/coloring.rs":"baf4861cabfc897755bef0b25a8367148bd2468717b3571763a5cc599407e5c2","src/regalloc/context.rs":"794f9f0fb3b980feb3be57b95c152767cb318b81aca48c27197b01b7d529c48d","src/regalloc/diversion.rs":"cbb942a197081f9e537f3fed71a7ec6d6d1edc18b8536fa46a1dda316f46c776","src/regalloc/live_value_tracker.rs":"054efd9e8da0f33a158082f67a7c2c14f7c8632b1fc28d54941ca7bc9d5a46d6","src/regalloc/liveness.rs":"6886e52e68aee5b2e26fb0b6e073905e7fa963eb597657fc966e5758cda4c254","src/regalloc/liverange.rs":"3c6a34e35a912dce48fa9a579e82e14a7e64334d5572b0d74dbfbf42dd380804","src/regalloc/mod.rs":"6254df639f9289fd578e01b7dca99bc9c9e3c6680c6d031405e8df8d0cff31ad","src/regalloc/pressure.rs":"7c73ca1f54559b1d9f8ce587bdc067e74f3d47901058f7ae1e9277516624236f","src/regalloc/register_set.rs":"294a66b676189e4da3cbb9c66474e0dd1a86a00b8cc71d03762994ae0654e11f","src/regalloc/reload.rs":"6525e9488e797b49b7892ac7596d6100793bb1db97c5025b17e6b32546fbe2fe","src/regalloc/solver.rs":"853c6f16f68e75add56981b132d92e556a94d8cbd13fed311df2d75da08de03a","src/regalloc/spilling.rs":"aae3faab22dcd8704179941a5b9b8e2b160d43a7daa02cebca1d31b9d368794b","src/regalloc/virtregs.rs":"23447a8e6196681cfb2388e0204626a0ac992735eb01f3eed4781c1cdfb688e4","src/result.rs":"d43abf9b22c5ad772200889bba339bcc4455c7abb9f2f3a2af65fbbaf1130009","src/scoped_hash_map.rs":"102797c380a588f7c16d26bf3b6c9cff28d37c2d8168a382b265194cd8969212","src/settings.rs":"18f3b43b5ec916b5969a62a6bbf0b7b8f039a5f7930e406bb7e8f181f2dc0d2d","src/simple_gvn.rs":"05576f7e90555c668c91df0b63a0468fbf2ed1aee9bc4118b73461f8719ead1a","src/simple_preopt.rs":"cfafd0398bba0b3e2b91b5bf10617bd4048ba2b22345b3e5bfc403239903960a","src/stack_layout.rs":"ce0b4b188cc46fa5070139124ffb2d263d44acd08703e7043a40b5c1afa952fb","src/timing.rs":"79acc12600ace2b144f1d7d82e01c023596e418a84042cf70ef93df928cdcabf","src/topo_order.rs":"73ec442db1cc9a282cf9c5b715ad2f60c4d2872080c16fd04ef7091d56816fbd","src/unreachable_code.rs":"6fdea8f9afe9fbffe411bfef48fec7937cb41d51036fd190a3d0a5f0cc2894c6","src/verifier/cssa.rs":"8f41765d18474575faa7c44a0345549dabe0af07141e2f004a7db1c67486ce77","src/verifier/flags.rs":"0665b4cbeef762c3871ba3cc55b0b1a27c513e25cbd3f93a725aa3d636a23c72","src/verifier/liveness.rs":"6e827f05461bd6fb21b0ce99f02fae374f2d6ea6e7e14ba61e88983a1c6fac4b","src/verifier/locations.rs":"a83f7d58118a838651c80f55e2455736e79235691692488d8431b77be8093a58","src/verifier/mod.rs":"4e17623e454bf115661ddaf0be0c2554e87dd4fc83a0a7a18dc8337d66ee5de5","src/write.rs":"1e810d79ff90aa37c76e16df8d6e69af6e35f5d7c6fae325243d8e2d0568ac4d"},"package":"e92fa0fa287cf00a6739c46aba114957e0a8eeeb4f0d1aa65d6ed0699c34ca6b"}
\ No newline at end of file
+{"files":{"Cargo.toml":"d9dfa768b55bf0c8d669f9a5e3075f222153672a04036b324ac6ce3cc5a4e047","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"e5127227a7db4a8aa92fa6613ed71801025790e696bb41b0323fb7f3c6f7495a","build.rs":"cb22f35d8804da1227f345d53e09a3ba5d62af60842e77629dcc9e370a6af080","meta-python/base/__init__.py":"4fac8bb055541dc964383bb04223bae6dfbfe348abf5e23f83655966cbb4aa8f","meta-python/base/entities.py":"0e146dd56dfb93cac88f9557a3501c9804cff584c2723db27435842bb5e2a1b7","meta-python/base/formats.py":"f9fb41210bc1f99a78cb7a60ee9f01c603412c6b1db7e69abbcc0f573cf9fb40","meta-python/base/immediates.py":"f42682d86bda7b569ec2fc2debd9036355999e61caaa9fbf8307e0be8a164814","meta-python/base/instructions.py":"4874aae8ea4ef3a1ac59fe9fc7b18c031ac9c72e3559c1c834df76c7144aa5c4","meta-python/base/legalize.py":"57b71ec3599fbf998c1926e4b48566661b7feec01dbd8bd177d0d2aaa26a44c2","meta-python/base/predicates.py":"53a5a9a37021a9762f0faec9f9e3c5b713411d47dd9192423cfe0f7a61916809","meta-python/base/semantics.py":"b90cbca9a143676ac37496c83634a66a83360248e4505ff7bda12b0d454efd92","meta-python/base/settings.py":"97fca9ddaab347f50f1594c93283f8f474e53a7232eae65949f56a6a702c2bba","meta-python/base/types.py":"9616d6fe4cab050827ab02eeb9843eacebbb8f7555521f504b5ee2ddf7214fdb","meta-python/build.py":"b72a80a54e09366878e92dca3a1508af394bf71a0c4b07a05e54058901373d34","meta-python/cdsl/__init__.py":"b531693b8228553ca8bd07e1dcd1aa5855f1ad3741b431d758fc27fdd162b024","meta-python/cdsl/ast.py":"b7c09f69b28b5754f494912ca5b77722dbd4ee416d5fad79cb48322eb544ea9f","meta-python/cdsl/formats.py":"fedfeaec754b40d6a9cc92b827976782c615d8eab1c7f47f6b47510cbef585f4","meta-python/cdsl/instructions.py":"b0ddfd8fd22889bd1e610c103d47087c9c6ae5882862ed44921dafc2ae0463a0","meta-python/cdsl/isa.py":"dc530a4dd5642e3379398dfc8a90ad6ae1692302de63851370bdfa8abf4cdce0","meta-python/cdsl/operands.py":"e24914eae4059b88781bf5a5d7a14ecf98b10a701ed6cf6e88d15981b2ccbfdf","meta-python/cdsl/predicates.py":"def3f91712744671df9cf3d4f9c255a2061f341198689e31addf7db4efb63e36","meta-python/cdsl/registers.py":"939dafd1b8976a6cd456c9a5e4b374af81fafb9da979ea3a1f6d14e4645914a6","meta-python/cdsl/settings.py":"a859bd006e13b245ff906977512cd4822c3b22601ed17e87ce0618503172809f","meta-python/cdsl/test_ast.py":"947e934e2862445a158bf266dff58a8c88aae31fb34a7f823309ee58a15c5393","meta-python/cdsl/test_package.py":"ffa53d20e023ecb89137294bb13614f4de9b09e1bf05d9772131570bf78f7987","meta-python/cdsl/test_ti.py":"57966c9eb027fa73bbc7ad81094ff55232053fbb2e16d78849ae3b6e9b1c2989","meta-python/cdsl/test_typevar.py":"714b2d564af1a843629279501436d681cd6590d1988b59e9f50ec940506538bb","meta-python/cdsl/test_xform.py":"ddb6633c7941bbf68570701cb887a81d6b4b27f4bc45eabccf2ce287ad9b77e9","meta-python/cdsl/ti.py":"a1a7ff79f8a2196aee491a3aafbd4f1b231004bbb5281992fc8f4e43f4fb951f","meta-python/cdsl/types.py":"adee4bbc1a9478288fa4b53ee1edeb5ee3296dba2c70bfbe01e923895064999e","meta-python/cdsl/typevar.py":"b5669934eddaf5b9cc0c27b966e2566b5f669f1c5a345f005960930fb499097e","meta-python/cdsl/xform.py":"5cdad80f12d50aa9491cd3d39797e6c0444936bb0874199d7c34e6d735658b34","meta-python/check.sh":"707cda14534882e8d4593f93c056390560394c9bc8a687e99f10a56d7b09cb92","meta-python/constant_hash.py":"c752e6dadf3a9a5bd00c978e85ab27a20c49138a1ccdc6fc9a1904797a4bfe48","meta-python/gen_binemit.py":"76472fb199a330b934ba9ad0a1bbacfb52f0eae7c9a66d83f0d7890970323a2d","meta-python/gen_build_deps.py":"3920c5c89451c26102f7d87c61de64c94e915a545bc8a35d2e49106aecf019ec","meta-python/gen_encoding.py":"0b57d9d74a72a1b3b11721166fdbaa8b8c4b2d7493fc10b88736ac330b83256f","meta-python/gen_instr.py":"7ccd6a6bb1ce1800ea9c2c37e462ac7ded4f908e311d708080f7d21d92578316","meta-python/gen_legalizer.py":"187a47f1702e07487fb8a13076aadcf9c2de3e801d367424dc847ff7b0ed70f1","meta-python/gen_settings.py":"c2fce1d2f3811bf11ab07cbabf4fca0250e01aa4b09ab55ec7fa251bf8ca28fa","meta-python/isa/__init__.py":"e499c1206cd095a926fa0ca7eb9d0a50a802ed71c8eb7598e5d3a0f939c8ada5","meta-python/isa/arm32/__init__.py":"eecba73231aa398ded7304690bdba3450dc163afd4360f1b0ad02a28e2380363","meta-python/isa/arm32/defs.py":"01c41dbd7406c624e26229df6befa0992194bddcc7d11e8f6174abfe2b33bf61","meta-python/isa/arm32/registers.py":"c03ca6435828ad5f262049e42f1f71bcf74903831f85daa92c3f322a6c1050ea","meta-python/isa/arm32/settings.py":"afd5a04a9d029f578d6f62dc7c539191886cc9f9dea15d65fc66bf37a63b8814","meta-python/isa/arm64/__init__.py":"f6877253cf786d7ee972881e7d9b3c78c11e6b024e4e227487340dd01d0c44e4","meta-python/isa/arm64/defs.py":"797c5bb6d11fc7a44afe67476136dbd11c40f5e13a1c8f52f9f96be4441677b2","meta-python/isa/arm64/registers.py":"9bdd06edaa382be96042e1ac36d63137e73292292b61dcf4becb7d1428130317","meta-python/isa/arm64/settings.py":"f7b1f8733e775ea8005372ee35f1c2a627b3a69d722e837295599e4cf1f5eb43","meta-python/isa/riscv/__init__.py":"c11607c9eef0bc2707daa3edd4174e934c7a0dcc8ce90cee2c9292a85b1ac596","meta-python/isa/riscv/defs.py":"e73740055c4fb123c45453fc149a807e9720466de848022d5375049bdcfc311c","meta-python/isa/riscv/encodings.py":"ecaad5ea98273ade1cb10606354e893342c495bb48771df50121f789566d7be6","meta-python/isa/riscv/recipes.py":"3852e5b7aa6995fa721ba91744a0470343ce1834651e7b9cc97b5d69af7dfdc5","meta-python/isa/riscv/registers.py":"ef9aca3a6ec2b08ee8f5952186d232861b64a919b671b41911a365e7672b01bd","meta-python/isa/riscv/settings.py":"dfe29722d67be0620a70e08cfb802829a26f5fd339a9342a8ac2dd419daf8a85","meta-python/isa/x86/__init__.py":"ad579de68ea7bf5dc2bce0e3a6f09e7978b1697f1afec8a5ce5dc591136e590d","meta-python/isa/x86/defs.py":"b5eb7889b6f5e5b221ed3923d0137bbb1566c55b5961448cc39e4ea2f13cf4b7","meta-python/isa/x86/encodings.py":"3885bcb2558e754de812f8239b4b509a32a42bcb8d3158d0b8899b18f3d90ac5","meta-python/isa/x86/instructions.py":"530cde78e6b9f6e4ea2192985f4c5c77a987cdc19001d50fb47fa8e36a62f52e","meta-python/isa/x86/legalize.py":"1375ded072c29459e7c0e40ecb02f28d5395d9d8c603eb70e338b2bf2991c9cd","meta-python/isa/x86/recipes.py":"37fd53909174f9f8cc35156bd06b8e82d75d353d67586f169f5dfdf2bb39a805","meta-python/isa/x86/registers.py":"ff934491d07ec6b51fbfd454b865a7c7c191ffbd31b1804615735266b120f4b2","meta-python/isa/x86/settings.py":"d779a768475cf00c2a8d3ddb5cd0a70ce34662e0ebb52ee26a7e1a495ec41aa2","meta-python/mypy.ini":"5ec2f7cc0bbc4fd0435643d6b72e715bd9568a3a0fe14c043f9e559c405b66fb","meta-python/semantics/__init__.py":"e8a25a111f2d9cc9fc7aa498a572a86403d31fe50a7ba59dd2e2560a17804e92","meta-python/semantics/elaborate.py":"3a3fbba83a6818c2d1ce236fd0413111380875a0307f7a5f4b5dd66d8ef714b1","meta-python/semantics/macros.py":"b218c52e1bd4f019dc14a27d315b4f3405a10e5bdc6f2523fe709c8faf91b418","meta-python/semantics/primitives.py":"4e5eb0c90fcc295686732c8c66ad7a793997645c9a676c97babf06823fd2b60d","meta-python/semantics/smtlib.py":"825edfbb9221bf59c02fea26e55d17cf32194da7a9f56ed0e035c44353481055","meta-python/semantics/test_elaborate.py":"3a4c850a7385007422c7549661b211903cd1dd1606dad7a86262ae27e697bca6","meta-python/srcgen.py":"999557d683e808a2ca90688c489ec4aff65798f44ac321ecf7de34d307261913","meta-python/stubs/z3/__init__.pyi":"6aaeb80f783b29c4364dee21da45f6df041c0a4807189a15777ee5447f6515dc","meta-python/stubs/z3/z3core.pyi":"c01a41d468e07cc4f8b405c292ed7f8c82bc1077f8b82dfde1e474577ade3335","meta-python/stubs/z3/z3types.pyi":"30009c951af99b9028d47cd4cabae95ff9742b77b690bd8dd63f6b7dba580759","meta-python/test_constant_hash.py":"157cf4f8964e0f04c041ffd349e889ce565b144453436690578c5d03c3a60216","meta-python/test_gen_legalizer.py":"f16edce7cb2ce53e55b1fc56b6f5ba6a0fc61b291ee4513ec859e36d69f0e285","meta-python/test_srcgen.py":"d6d7775e19a5b2621360c00eb6d92dfcb4568e49220993e0ceaac9628dbfd661","meta-python/unique_table.py":"5bd500667430c15f6ae586603d8612fb3bda07b072e40d86286e08392bdc3127","src/abi.rs":"29f505fdfcb6ec14e561bb408b99ab30ab69b96d1e283a1dcd55db9aff02dbe4","src/binemit/memorysink.rs":"8437e5f5c1b3e68b8e2d0de6fb3a4982f7a06390a0f8820b6379492408df8964","src/binemit/mod.rs":"2f95ea5f6ee20b8d56bdedcacdd41a609c5b999c02affca341d6a71eb59bc113","src/binemit/relaxation.rs":"c814a42504e849d5c29fd935d8e4c74146f802f775e96f7df4a86576bd942660","src/binemit/shrink.rs":"428679a02e44a7b3621a43c7d2e8d2f3b2cb50a36179a4d9862205c0ba34ae50","src/bitset.rs":"67fd02dd2acb9af73e45426e5cab1d2a74d568a11d11ee17ae6a4421f96cf741","src/cfg_printer.rs":"8c0fda88060c204985a6406ba46d7f0a69be96bb18c9fcfc70055d50986068ab","src/constant_hash.rs":"442c010f832191225c3d21815e99bd087cfd3367e5a8ffe6204e75f53bd355c9","src/context.rs":"a44197bd5e05599b643c420d87ca4ad218a8c3ab2fb7b88655b2479014b117b2","src/cursor.rs":"523899307d471f63e9ca35ee4b3340cf55041561a05b199e11cc60f6ad2714f4","src/dbg.rs":"bae915e1f7544f725817b368aed00a6aaa9e394a454dc847d65ad44e54d78cb9","src/dce.rs":"6d015824ae0325f01bb523606d7e15dd5feeb72a75b947f40385feeba94555c8","src/divconst_magic_numbers.rs":"eac50e2353e17ab6f7d1da3bd8e0e8dc9e3122a160efba594d407eb6a8936cc7","src/dominator_tree.rs":"73032e0e7a0ab694aa3181222bccb40630bd6fbda33885a391fd662f7de7d540","src/flowgraph.rs":"fccfade2b24038e18d469e273976f418757159fde140642b5faeb3c664a756ce","src/fx.rs":"2fb53f141b3e6be1882a1e4afac4bc607ce694f045d9329ee823e0aca415898e","src/ir/builder.rs":"19aa7cef76b7577bdd9d769fb1260080a959f9bfdbac569fb1e38307a721a03c","src/ir/condcodes.rs":"5456a25798516245b0923c9e12ae0af5cc98c097fc6f3bc0bf2d7af5008667f7","src/ir/dfg.rs":"f5cd6efb9cfc158d3f13671e7d739746f1f009230ea0865baac5edc3f32a0122","src/ir/entities.rs":"1505347c2baa256b04f69384d1391f52e007d533cb4319d3826cf3046ec1df27","src/ir/extfunc.rs":"9a3535730a39a6b71ca9f1ed679f588e6c3fa48ee7a50489d90803f3134db4a6","src/ir/extname.rs":"5b50dddd0801dfe736433191502e405c879221a6e362be0e2919176b90b4d74c","src/ir/function.rs":"55feb0b0a2bf4a0d194a4d11fc297616c78d5edfa41504795742fd25e7af1399","src/ir/globalvalue.rs":"bf9b76430f6ba564d4e5db85dbebfddf952678be6914326a5549b569c064d03d","src/ir/heap.rs":"2e6c5ed8f4a1ac3cd37e5115ae978d48d61b9af100da0cff1b1b6d696907958e","src/ir/immediates.rs":"ccf1210052346a62a0914251a2da8c40e75b00e58102ef7975719b79eb644dd2","src/ir/instructions.rs":"2dd2ad70947122e791f41f6cbc7879fd201d4f96d19c138b56559428ba655dab","src/ir/jumptable.rs":"3fc108096e5404c4c30220cfaa4e359caa83bad747268bea88bbc0ac4b1bcd13","src/ir/layout.rs":"77210d256e39d339736ced5518c8d666da6359f656b972d6b6b19581ccaec329","src/ir/libcall.rs":"b35c8d0c90e686a176f9bd157ef6ab3819e8a0b974d0d24007a910ffb0d15f51","src/ir/memflags.rs":"26654b642e36bf11c0871e2b3e20cbdbaf8e53218a0bfef7b7bcfd9e838b51fc","src/ir/mod.rs":"16566fb92fc16498d5a2699fa76dfdbc39665a4e8bae3040c03e4f3d2f07e3cb","src/ir/progpoint.rs":"911046269d250102f8ae473411fda7ca7f1acd3d48a9392cacfe3b2e5188c321","src/ir/sourceloc.rs":"79eb71609b844839994fc735cd72edf53179eb659303a4150634928e207cee4f","src/ir/stackslot.rs":"d5d0c61555bf7060c58603047356b7db53b5b296497daed8eac356b9e724902d","src/ir/table.rs":"3550a5fb5af0d4992b4c7d23a3e790a411ad39c1351fa03b54add691e6232163","src/ir/trapcode.rs":"9d78e03784dbdaffb60228e82f4f6277dd78422668e9afa7614a0552158a93cd","src/ir/types.rs":"a56cf1844817171276907fa18f973783d4b6223e77bf3ee94dce7db442946433","src/ir/valueloc.rs":"5055897d9acba6d9c396b126889f9b2c7ff3f54a27c1ec5fe70d1a059d633b36","src/isa/arm32/abi.rs":"50ca3161a0f11ba1c2d5b0ff7523d332503cb6a6182695246e4284a486e18cab","src/isa/arm32/binemit.rs":"3197df7b15c223f3a9f2708311375ff423bb247381cf21e26da079f7933f5279","src/isa/arm32/enc_tables.rs":"dacb50bdacfa2085a6283818563e19874590d223e5decb5c91b968e0348e2bf7","src/isa/arm32/mod.rs":"798049325ca439444a5508f63ebb53bf2e0ab16b128c43417017bbdb2fd95742","src/isa/arm32/registers.rs":"0e5c32a218d685c6d37fb46788baedf6bede6be7d4f715808c13620636dfc472","src/isa/arm32/settings.rs":"145f59227c6087e7872f66a6d2183c66e061c40466e7b427a733136e41e41207","src/isa/arm64/abi.rs":"bfd0065a6c26eb407872959c9d7f64169591afa4d69816370e0900aa1ad4e46f","src/isa/arm64/binemit.rs":"159ab9eca03ac4aa7f55d51ab20585a5971d95a74c61953d1899ac863b06e2ec","src/isa/arm64/enc_tables.rs":"00ec8f53402f6cb73716db1adb3aca564574833cab58dc12b4dc6ba8c4529a73","src/isa/arm64/mod.rs":"4eef5f904f2219262c59dae7056af5adb7764074309dffc0be5b2357781dd1a6","src/isa/arm64/registers.rs":"7a8b207ed28737efc737e8e65ce79e7e7d3eecd23599890230dca8474c985026","src/isa/arm64/settings.rs":"7b466fcc2c58bc1846a72f08ec58900b3cf622ab2922ee9128e6dfe254907bf5","src/isa/call_conv.rs":"00d8ea90af7bc414d8bbe4eadcff4484c53ec74979a8677ff5e215d17d43dcac","src/isa/constraints.rs":"324d7c8655565f13b90a6c9587c9fb6c545ab3086201f434e1324fc5d6e5b3c7","src/isa/enc_tables.rs":"946a9fcdf7d0541e7e65aeafdf18b6a5cb9e3ea29a1ce3bf05c155f910acca3a","src/isa/encoding.rs":"032347d4e624449af7a35f7ba012339bc49fabae97b971818e922d6ca4510e59","src/isa/mod.rs":"da05b44fd82305c4b2fdcc16a7dbcc5281cf919cf6df09c305561e6e7772c719","src/isa/registers.rs":"dc0ab116f10adc0aa27bc9b7c688106966d221e0654bec7ec53ceaea3a8b784b","src/isa/riscv/abi.rs":"1de6d0070126e0127ca6a63551a14d1b5c030cf9628538fd732301fd2bd34b5e","src/isa/riscv/binemit.rs":"3bdad2791447f51bfe5ecedb73b1aed8a6a8809790b1f26e3ff624a89163a026","src/isa/riscv/enc_tables.rs":"6bc179f95ef5634b64fb42ab67929c0aeb70ac8efccfc47dd8c9b1dbfe64a446","src/isa/riscv/mod.rs":"de0dd32005f6b0510e3c8a31bb7e58ab7e7cffb674169a08558794950db87126","src/isa/riscv/registers.rs":"794ac61343a8db66dc99b2ca964ea3986b47b37fe8b72575c644c1cdaabd2991","src/isa/riscv/settings.rs":"78ced69b82d89f83b5612a91d0acdac2616f9091b380ee3e4715adb31f95b232","src/isa/stack.rs":"ec96130c446cd7d637722f1603e38d5164498368b652f4b0454baf3227385ad4","src/isa/x86/abi.rs":"c11c288c59edf834043b06eda3a271394e0dba6c719900f0a201ec6108197b69","src/isa/x86/binemit.rs":"f90820a1f9c4ad02f3507625d35f014f644d8cea93b0216bcc60cc05cc84eacc","src/isa/x86/enc_tables.rs":"aa4bd2992d08ad97e94aafa022cafd853e0978c7c139558885715f57bfcf49e7","src/isa/x86/mod.rs":"bc83c0e619e8603885359c472b4113bc83b8867dd2d8e83b27f798301c6df93c","src/isa/x86/registers.rs":"783ebbe4c3b4711fe1eac2cfd3bbea7c31a53c15c6028397c038ef4eb0b2aa06","src/isa/x86/settings.rs":"dcce098045c1115cd55f256dcd3e0ccaa5a0c9ad93b14fb8a4c680e95caf2b73","src/iterators.rs":"f85f52d3fa707a0eb974c92215b3e976923ce8f9481219f7812e0f2869c2bd37","src/legalizer/boundary.rs":"cbc6035ac90b9d2c8e621e3a058e3120afa144ee07f901d3370d01340e0331f9","src/legalizer/call.rs":"12f380d126765f0bc2da1cf298088fa98451e2d6bf56c34b755ff7077d14a4f1","src/legalizer/globalvalue.rs":"a8b7784692354cbe3577532103171bf9ca8a22aa077c6d06d91b055eb4be4f8a","src/legalizer/heap.rs":"86f642d07ac65fd29de6ad23907849e13747bc90b4c4fb18039d6ff3cfd2dc8a","src/legalizer/libcall.rs":"bf18267f502bf9bfbc3ff51bc05df480293a84422ea7f4c37dd0c75e97952766","src/legalizer/mod.rs":"bb79f504a3bfce92c4787d9c2a9385d81b0c02eb15fbe01ff66f355d95348c3a","src/legalizer/split.rs":"2e0ab9c35006d3743bdbd1ee6a4332b05af2c3e81137fed102422bac4a5ebc3f","src/legalizer/table.rs":"a695262e045e71456c641b576eeebed0c8666495a7e1a70abe60f861b599fae5","src/lib.rs":"7e08cb847189bd7faa3dd205867505f7f8ed97671d32e392848ec0d71a1df422","src/licm.rs":"dcdf4744a01489939955dd02d43be3522cfbc448d92b30796d512b10e784c503","src/loop_analysis.rs":"ab74f702649ddd16d3d91400c3c2aafed4f09d9af210f5e180dff15a82caf8ac","src/nan_canonicalization.rs":"e2b1f9935d14af9abe553de1eda5483cffdaa7098dd1a17ba9a98fa04280b22c","src/partition_slice.rs":"55b9e857f452baf4d6312779861f9ef93b3d5300467e752230ec6af14943efe3","src/postopt.rs":"f1fe06398c644cad19fd427323c74acdb237b5853ffb72148a2b2012b906e2a3","src/predicates.rs":"8e4c4afde65420d33621aedd80e4ce270d334c1b62a17d7879273a1719d49b41","src/print_errors.rs":"9db532d3ee4bb5d8265cbf36d371f2941a6dc0ea6f86d663dcdf54ede1bedd21","src/ref_slice.rs":"9be914b705eefcc6b4e38db573f56ad7b978f4b65c2f6f8f5516df78fb2206a4","src/regalloc/affinity.rs":"47e28f9ae7cebfc9b1006480143a3a5ab1b118ec761a163c4a670b63fdc47d0a","src/regalloc/coalescing.rs":"5ffab5529e5840e0dc3f13930788caf75ae9e15ce90d4f64f9f625b2ee495605","src/regalloc/coloring.rs":"beb4893d82e0a7965417c158c51e3752896b591f9db7d1c4803caa2b34154a29","src/regalloc/context.rs":"6ff7bfc76c63ff18850c1354c1e5e72287cc0a7d65d52ef9804223c750a09f7c","src/regalloc/diversion.rs":"cbb942a197081f9e537f3fed71a7ec6d6d1edc18b8536fa46a1dda316f46c776","src/regalloc/live_value_tracker.rs":"054efd9e8da0f33a158082f67a7c2c14f7c8632b1fc28d54941ca7bc9d5a46d6","src/regalloc/liveness.rs":"b21e4855c0339c1b15b1b62e63bb1a569c4256b42d08c3c21b6e7fc4634c5e62","src/regalloc/liverange.rs":"3c6a34e35a912dce48fa9a579e82e14a7e64334d5572b0d74dbfbf42dd380804","src/regalloc/mod.rs":"6254df639f9289fd578e01b7dca99bc9c9e3c6680c6d031405e8df8d0cff31ad","src/regalloc/pressure.rs":"7c73ca1f54559b1d9f8ce587bdc067e74f3d47901058f7ae1e9277516624236f","src/regalloc/register_set.rs":"dcdf30ec4ffeac26eabf85f585209a04617eef00bf59e6a1c0462f1dc3454700","src/regalloc/reload.rs":"714e598e968ca9498da969af2343cab7983ddbe160cef0a2d36543618885acf7","src/regalloc/solver.rs":"853c6f16f68e75add56981b132d92e556a94d8cbd13fed311df2d75da08de03a","src/regalloc/spilling.rs":"6ab899e036a50965b0b7d5fa9aef9077c68ae6932315ec2e14d71c2f3791d78f","src/regalloc/virtregs.rs":"5b12892ad360f8c2a8f7b1ffe40e467a88e64aa1c77c8012149c6653e46fb3d6","src/result.rs":"d43abf9b22c5ad772200889bba339bcc4455c7abb9f2f3a2af65fbbaf1130009","src/scoped_hash_map.rs":"102797c380a588f7c16d26bf3b6c9cff28d37c2d8168a382b265194cd8969212","src/settings.rs":"18f3b43b5ec916b5969a62a6bbf0b7b8f039a5f7930e406bb7e8f181f2dc0d2d","src/simple_gvn.rs":"05576f7e90555c668c91df0b63a0468fbf2ed1aee9bc4118b73461f8719ead1a","src/simple_preopt.rs":"cfafd0398bba0b3e2b91b5bf10617bd4048ba2b22345b3e5bfc403239903960a","src/stack_layout.rs":"ce0b4b188cc46fa5070139124ffb2d263d44acd08703e7043a40b5c1afa952fb","src/timing.rs":"7c05d910efbbca4138678cd463d5a1709bedc2c3fdbea239f3a47798252af05f","src/topo_order.rs":"73ec442db1cc9a282cf9c5b715ad2f60c4d2872080c16fd04ef7091d56816fbd","src/unreachable_code.rs":"6fdea8f9afe9fbffe411bfef48fec7937cb41d51036fd190a3d0a5f0cc2894c6","src/verifier/cssa.rs":"8f41765d18474575faa7c44a0345549dabe0af07141e2f004a7db1c67486ce77","src/verifier/flags.rs":"0665b4cbeef762c3871ba3cc55b0b1a27c513e25cbd3f93a725aa3d636a23c72","src/verifier/liveness.rs":"6e827f05461bd6fb21b0ce99f02fae374f2d6ea6e7e14ba61e88983a1c6fac4b","src/verifier/locations.rs":"a83f7d58118a838651c80f55e2455736e79235691692488d8431b77be8093a58","src/verifier/mod.rs":"9cad0ca7502bef8d99bb3519105630c364a276d6501fd6f3acbebd3692075efa","src/write.rs":"1e810d79ff90aa37c76e16df8d6e69af6e35f5d7c6fae325243d8e2d0568ac4d"},"package":"42f5b809bd885c368e01aeec8fe04f21dcb07569834b907d75b4a7bed8d067eb"}
\ No newline at end of file
--- a/third_party/rust/cranelift-codegen/Cargo.toml
+++ b/third_party/rust/cranelift-codegen/Cargo.toml
@@ -7,32 +7,32 @@
 #
 # 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]
 name = "cranelift-codegen"
-version = "0.25.0"
+version = "0.26.0"
 authors = ["The Cranelift Project Developers"]
 build = "build.rs"
 description = "Low-level code generator library"
 documentation = "https://cranelift.readthedocs.io/"
 readme = "README.md"
 keywords = ["compile", "compiler", "jit"]
 categories = ["no-std"]
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/CraneStation/cranelift"
 [dependencies.cranelift-bforest]
-version = "0.25.0"
+version = "0.26.0"
 default-features = false
 
 [dependencies.cranelift-entity]
-version = "0.25.0"
+version = "0.26.0"
 default-features = false
 
 [dependencies.failure]
 version = "0.1.1"
 features = ["derive"]
 default-features = false
 
 [dependencies.failure_derive]
@@ -46,17 +46,17 @@ optional = true
 [dependencies.log]
 version = "0.4.4"
 default-features = false
 
 [dependencies.target-lexicon]
 version = "0.2.0"
 default-features = false
 [build-dependencies.cranelift-codegen-meta]
-version = "0.25.0"
+version = "0.26.0"
 
 [features]
 core = ["hashmap_core"]
 default = ["std"]
 std = ["cranelift-entity/std", "cranelift-bforest/std", "target-lexicon/std"]
 testing_hooks = []
 [badges.maintenance]
 status = "experimental"
--- a/third_party/rust/cranelift-codegen/build.rs
+++ b/third_party/rust/cranelift-codegen/build.rs
@@ -18,18 +18,21 @@
 // The build script expects to be run from the directory where this build.rs file lives. The
 // current directory is used to find the sources.
 
 extern crate cranelift_codegen_meta as meta;
 
 use meta::isa::Isa;
 use std::env;
 use std::process;
+use std::time::Instant;
 
 fn main() {
+    let start_time = Instant::now();
+
     let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set");
     let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set");
     let cranelift_targets = env::var("CRANELIFT_TARGETS").ok();
     let cranelift_targets = cranelift_targets.as_ref().map(|s| s.as_ref());
     let python = identify_python();
 
     // Configure isa targets cfg.
     match isa_targets(cranelift_targets, &target_triple) {
@@ -74,31 +77,45 @@ fn main() {
         process::exit(status.code().unwrap());
     }
 
     // DEVELOPMENT:
     // ------------------------------------------------------------------------
     // Now that the Python build process is complete, generate files that are
     // emitted by the `meta` crate.
     // ------------------------------------------------------------------------
-    let isas = meta::isa::define_all();
 
-    if let Err(err) = meta::gen_types::generate("types.rs", &out_dir) {
+    if let Err(err) = generate_meta(&out_dir) {
         eprintln!("Error: {}", err);
         process::exit(1);
     }
 
-    for isa in isas {
-        if let Err(err) = meta::gen_registers::generate(isa, "registers", &out_dir) {
-            eprintln!("Error: {}", err);
-            process::exit(1);
-        }
+    if let Ok(_) = env::var("CRANELIFT_VERBOSE") {
+        println!(
+            "cargo:warning=Build step took {:?}.",
+            Instant::now() - start_time
+        );
+        println!("cargo:warning=Generated files are in {}", out_dir);
     }
 }
 
+fn generate_meta(out_dir: &str) -> Result<(), meta::error::Error> {
+    let shared_settings = meta::gen_settings::generate_common("new_settings.rs", &out_dir)?;
+    let isas = meta::isa::define_all(&shared_settings);
+
+    meta::gen_types::generate("types.rs", &out_dir)?;
+
+    for isa in &isas {
+        meta::gen_registers::generate(&isa, "registers", &out_dir)?;
+        meta::gen_settings::generate(&isa, "new_settings", &out_dir)?;
+    }
+
+    Ok(())
+}
+
 fn identify_python() -> &'static str {
     for python in &["python", "python3", "python2.7"] {
         if process::Command::new(python)
             .arg("--version")
             .status()
             .is_ok()
         {
             return python;
--- a/third_party/rust/cranelift-codegen/meta-python/base/instructions.py
+++ b/third_party/rust/cranelift-codegen/meta-python/base/instructions.py
@@ -176,16 +176,20 @@ indirect_jump_table_br = Instruction(
     Branch indirectly via a jump table entry.
 
     Unconditionally jump via a jump table entry that was previously loaded
     with the ``jump_table_entry`` instruction.
     """,
     ins=(addr, JT),
     is_branch=True, is_indirect_branch=True, is_terminator=True)
 
+debugtrap = Instruction('debugtrap', r"""
+    Encodes an assembly debug trap.
+    """, can_load=True, can_store=True, other_side_effects=True)
+
 code = Operand('code', trapcode)
 trap = Instruction(
         'trap', r"""
         Terminate execution unconditionally.
         """,
         ins=code, is_terminator=True, can_trap=True)
 
 trapz = Instruction(
--- a/third_party/rust/cranelift-codegen/meta-python/cdsl/settings.py
+++ b/third_party/rust/cranelift-codegen/meta-python/cdsl/settings.py
@@ -335,17 +335,17 @@ class SettingGroup(object):
 
     def byte_size(self):
         # type: () -> int
         """
         Compute the number of bytes required to hold all settings and
         precomputed predicates.
 
         This is the size of the byte-sized settings plus all the numbered
-        predcate bits rounded up to a whole number of bytes.
+        predicate bits rounded up to a whole number of bytes.
         """
         return self.boolean_offset + (len(self.predicate_number) + 7) // 8
 
 
 class Preset(object):
     """
     A collection of setting values that are applied at once.
 
--- a/third_party/rust/cranelift-codegen/meta-python/check.sh
+++ b/third_party/rust/cranelift-codegen/meta-python/check.sh
@@ -12,17 +12,21 @@ function runif {
         echo "$1 not found"
     fi
 }
 
 # Style linting.
 runif flake8 .
 
 # Type checking.
-runif mypy --py2 build.py
+# TODO: Re-enable mypy on Travis osx. Pip currently installs mypy into a
+# directory which is not in the PATH.
+if [ "${TRAVIS_OS_NAME:-other}" != "osx" ]; then
+    runif mypy --py2 build.py
+fi
 
 # Python unit tests.
 runif python2.7 -m unittest discover
 
 # Then run the unit tests again with Python 3.
 # We get deprecation warnings about assertRaisesRegexp which was renamed in
 # Python 3, but there doesn't seem to be an easy workaround.
 runif python3 -Wignore:Deprecation -m unittest discover
--- a/third_party/rust/cranelift-codegen/meta-python/gen_settings.py
+++ b/third_party/rust/cranelift-codegen/meta-python/gen_settings.py
@@ -250,18 +250,18 @@ def gen_display(sgrp, fmt):
                     fmt.line('write!(f, "{} = ", d.name)?;')
                     fmt.line(
                             'TEMPLATE.format_toml_value(d.detail, ' +
                             'self.bytes[d.offset as usize], f)?;')
                     fmt.line('writeln!(f)?;')
             fmt.line('Ok(())')
 
 
-def gen_constructor(sgrp, parent, fmt):
-    # type: (SettingGroup, PredContext, srcgen.Formatter) -> None
+def gen_constructor(sgrp, fmt):
+    # type: (SettingGroup, srcgen.Formatter) -> None
     """
     Generate a Flags constructor.
     """
 
     with fmt.indented('impl Flags {', '}'):
         args = 'builder: Builder'
         if sgrp.parent:
             p = sgrp.parent
@@ -305,17 +305,17 @@ def gen_group(sgrp, fmt):
     """
     Generate a Flags struct representing `sgrp`.
     """
     fmt.line('#[derive(Clone)]')
     fmt.doc_comment('Flags group `{}`.'.format(sgrp.name))
     with fmt.indented('pub struct Flags {', '}'):
         fmt.line('bytes: [u8; {}],'.format(sgrp.byte_size()))
 
-    gen_constructor(sgrp, None, fmt)
+    gen_constructor(sgrp, fmt)
     gen_enum_types(sgrp, fmt)
     gen_getters(sgrp, fmt)
     gen_descriptors(sgrp, fmt)
     gen_template(sgrp, fmt)
     gen_display(sgrp, fmt)
 
 
 def generate(isas, out_dir):
--- a/third_party/rust/cranelift-codegen/meta-python/isa/x86/encodings.py
+++ b/third_party/rust/cranelift-codegen/meta-python/isa/x86/encodings.py
@@ -516,22 +516,27 @@ enc_both(base.brnz.b1, r.t8jccd_abcd, 0x
 X86_64.enc(base.jump_table_entry.i64.any.any, *r.jt_entry.rex(0x63, w=1))
 X86_32.enc(base.jump_table_entry.i32.any.any, *r.jt_entry(0x8b))
 
 X86_64.enc(base.jump_table_base.i64, *r.jt_base.rex(0x8d, w=1))
 X86_32.enc(base.jump_table_base.i32, *r.jt_base(0x8d))
 
 enc_x86_64(base.indirect_jump_table_br.i64, r.indirect_jmp, 0xff, rrr=4)
 X86_32.enc(base.indirect_jump_table_br.i32, *r.indirect_jmp(0xff, rrr=4))
+
 #
 # Trap as ud2
 #
 X86_32.enc(base.trap, *r.trap(0x0f, 0x0b))
 X86_64.enc(base.trap, *r.trap(0x0f, 0x0b))
 
+# Debug trap as int3
+X86_32.enc(base.debugtrap, r.debugtrap, 0)
+X86_64.enc(base.debugtrap, r.debugtrap, 0)
+
 # Using a standard EncRecipe, not the TailRecipe.
 X86_32.enc(base.trapif, r.trapif, 0)
 X86_64.enc(base.trapif, r.trapif, 0)
 X86_32.enc(base.trapff, r.trapff, 0)
 X86_64.enc(base.trapff, r.trapff, 0)
 
 #
 # Comparisons
--- a/third_party/rust/cranelift-codegen/meta-python/isa/x86/recipes.py
+++ b/third_party/rust/cranelift-codegen/meta-python/isa/x86/recipes.py
@@ -290,16 +290,21 @@ def valid_scale(iform):
               IsEqual(iform.imm, 4),
               IsEqual(iform.imm, 8))
 
 
 # A null unary instruction that takes a GPR register. Can be used for identity
 # copies and no-op conversions.
 null = EncRecipe('null', Unary, base_size=0, ins=GPR, outs=0, emit='')
 
+debugtrap = EncRecipe('debugtrap', NullAry, base_size=1, ins=(), outs=(),
+                      emit='''
+                      sink.put1(0xcc);
+                      ''')
+
 # XX opcode, no ModR/M.
 trap = TailRecipe(
         'trap', Trap, base_size=0, ins=(), outs=(),
         emit='''
         sink.trap(code, func.srclocs[inst]);
         PUT_OP(bits, BASE_REX, sink);
         ''')
 
--- a/third_party/rust/cranelift-codegen/src/binemit/relaxation.rs
+++ b/third_party/rust/cranelift-codegen/src/binemit/relaxation.rs
@@ -181,17 +181,18 @@ fn relax_branch(
                 // validity directly because we don't have a RegDiversions active so
                 // we don't know which registers are actually in use.
                 debug!("  trying [{}]: constraints differ", encinfo.display(enc));
                 false
             } else {
                 debug!("  trying [{}]: OK", encinfo.display(enc));
                 true
             }
-        }) {
+        })
+    {
         cur.func.encodings[inst] = enc;
         return encinfo.byte_size(enc, inst, &divert, &cur.func);
     }
 
     // Note: On some RISC ISAs, conditional branches have shorter range than unconditional
     // branches, so one way of extending the range of a conditional branch is to invert its
     // condition and make it branch over an unconditional jump which has the larger range.
     //
--- a/third_party/rust/cranelift-codegen/src/constant_hash.rs
+++ b/third_party/rust/cranelift-codegen/src/constant_hash.rs
@@ -51,17 +51,17 @@ pub fn probe<K: Copy + Eq, T: Table<K> +
         // entries. This means that this loop will always terminate if the hash table has even
         // one unused entry.
         debug_assert!(step < table.len());
         idx += step;
     }
 }
 
 /// A primitive hash function for matching opcodes.
-/// Must match `lib/codegen/meta-python/constant_hash.py`.
+/// Must match `lib/codegen/meta-python/constant_hash.py` and `lib/codegen/meta/constant_hash.rs`.
 pub fn simple_hash(s: &str) -> usize {
     let mut h: u32 = 5381;
     for c in s.chars() {
         h = (h ^ c as u32).wrapping_add(h.rotate_right(6));
     }
     h as usize
 }
 
--- a/third_party/rust/cranelift-codegen/src/ir/dfg.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/dfg.rs
@@ -797,19 +797,19 @@ impl DataFlowGraph {
         } else {
             panic!("{} must be an EBB parameter", val);
         };
         self.ebbs[ebb]
             .params
             .remove(num as usize, &mut self.value_lists);
         for index in num..(self.num_ebb_params(ebb) as u16) {
             match self.values[self.ebbs[ebb]
-                                  .params
-                                  .get(index as usize, &self.value_lists)
-                                  .unwrap()]
+                .params
+                .get(index as usize, &self.value_lists)
+                .unwrap()]
             {
                 ValueData::Param { ref mut num, .. } => {
                     *num -= 1;
                 }
                 _ => panic!(
                     "{} must be an EBB parameter",
                     self.ebbs[ebb]
                         .params
--- a/third_party/rust/cranelift-codegen/src/ir/heap.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/heap.rs
@@ -1,26 +1,26 @@
 //! Heaps.
 
-use ir::immediates::Imm64;
+use ir::immediates::Uimm64;
 use ir::{GlobalValue, Type};
 use std::fmt;
 
 /// Information about a heap declaration.
 #[derive(Clone)]
 pub struct HeapData {
     /// The address of the start of the heap's storage.
     pub base: GlobalValue,
 
     /// Guaranteed minimum heap size in bytes. Heap accesses before `min_size` don't need bounds
     /// checking.
-    pub min_size: Imm64,
+    pub min_size: Uimm64,
 
-    /// Size in bytes of the guard pages following the heap.
-    pub guard_size: Imm64,
+    /// Size in bytes of the offset-guard pages following the heap.
+    pub offset_guard_size: Uimm64,
 
     /// Heap style, with additional style-specific info.
     pub style: HeapStyle,
 
     /// The index type for the heap.
     pub index_type: Type,
 }
 
@@ -29,20 +29,20 @@ pub struct HeapData {
 pub enum HeapStyle {
     /// A dynamic heap can be relocated to a different base address when it is grown.
     Dynamic {
         /// Global value providing the current bound of the heap in bytes.
         bound_gv: GlobalValue,
     },
 
     /// A static heap has a fixed base address and a number of not-yet-allocated pages before the
-    /// guard pages.
+    /// offset-guard pages.
     Static {
-        /// Heap bound in bytes. The guard pages are allocated after the bound.
-        bound: Imm64,
+        /// Heap bound in bytes. The offset-guard pages are allocated after the bound.
+        bound: Uimm64,
     },
 }
 
 impl fmt::Display for HeapData {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str(match self.style {
             HeapStyle::Dynamic { .. } => "dynamic",
             HeapStyle::Static { .. } => "static",
@@ -50,13 +50,13 @@ impl fmt::Display for HeapData {
 
         write!(f, " {}, min {}", self.base, self.min_size)?;
         match self.style {
             HeapStyle::Dynamic { bound_gv } => write!(f, ", bound {}", bound_gv)?,
             HeapStyle::Static { bound } => write!(f, ", bound {}", bound)?,
         }
         write!(
             f,
-            ", guard {}, index_type {}",
-            self.guard_size, self.index_type
+            ", offset_guard {}, index_type {}",
+            self.offset_guard_size, self.index_type
         )
     }
 }
--- a/third_party/rust/cranelift-codegen/src/ir/immediates.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/immediates.rs
@@ -4,17 +4,17 @@
 //! Each type here should have a corresponding definition in the `cranelift.immediates` Python
 //! module in the meta language.
 
 use std::fmt::{self, Display, Formatter};
 use std::mem;
 use std::str::FromStr;
 use std::{i32, u32};
 
-/// 64-bit immediate integer operand.
+/// 64-bit immediate signed integer operand.
 ///
 /// An `Imm64` operand can also be used to represent immediate values of smaller integer types by
 /// sign-extending to `i64`.
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 pub struct Imm64(i64);
 
 impl Imm64 {
     /// Create a new `Imm64` representing the signed number `x`.
@@ -35,58 +35,128 @@ impl Into<i64> for Imm64 {
 }
 
 impl From<i64> for Imm64 {
     fn from(x: i64) -> Self {
         Imm64(x)
     }
 }
 
+impl Display for Imm64 {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        let x = self.0;
+        if -10_000 < x && x < 10_000 {
+            // Use decimal for small numbers.
+            write!(f, "{}", x)
+        } else {
+            write_hex(x as u64, f)
+        }
+    }
+}
+
+/// Parse a 64-bit signed number.
+fn parse_i64(s: &str) -> Result<i64, &'static str> {
+    let negative = s.starts_with('-');
+    let s2 = if negative || s.starts_with('+') {
+        &s[1..]
+    } else {
+        s
+    };
+
+    let mut value = parse_u64(s2)?;
+
+    // We support the range-and-a-half from -2^63 .. 2^64-1.
+    if negative {
+        value = value.wrapping_neg();
+        // Don't allow large negative values to wrap around and become positive.
+        if value as i64 > 0 {
+            return Err("Negative number too small");
+        }
+    }
+    Ok(value as i64)
+}
+
+impl FromStr for Imm64 {
+    type Err = &'static str;
+
+    // Parse a decimal or hexadecimal `Imm64`, formatted as above.
+    fn from_str(s: &str) -> Result<Self, &'static str> {
+        parse_i64(s).map(Self::new)
+    }
+}
+
+/// 64-bit immediate unsigned integer operand.
+///
+/// A `Uimm64` operand can also be used to represent immediate values of smaller integer types by
+/// zero-extending to `i64`.
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+pub struct Uimm64(u64);
+
+impl Uimm64 {
+    /// Create a new `Uimm64` representing the unsigned number `x`.
+    pub fn new(x: u64) -> Self {
+        Uimm64(x)
+    }
+
+    /// Return self negated.
+    pub fn wrapping_neg(self) -> Self {
+        Uimm64(self.0.wrapping_neg())
+    }
+}
+
+impl Into<u64> for Uimm64 {
+    fn into(self) -> u64 {
+        self.0
+    }
+}
+
+impl From<u64> for Uimm64 {
+    fn from(x: u64) -> Self {
+        Uimm64(x)
+    }
+}
+
 /// Hexadecimal with a multiple of 4 digits and group separators:
 ///
 ///   0xfff0
 ///   0x0001_ffff
 ///   0xffff_ffff_fff8_4400
 ///
-fn write_hex(x: i64, f: &mut Formatter) -> fmt::Result {
+fn write_hex(x: u64, f: &mut Formatter) -> fmt::Result {
     let mut pos = (64 - x.leading_zeros() - 1) & 0xf0;
     write!(f, "0x{:04x}", (x >> pos) & 0xffff)?;
     while pos > 0 {
         pos -= 16;
         write!(f, "_{:04x}", (x >> pos) & 0xffff)?;
     }
     Ok(())
 }
 
-impl Display for Imm64 {
+impl Display for Uimm64 {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         let x = self.0;
-        if -10_000 < x && x < 10_000 {
+        if x < 10_000 {
             // Use decimal for small numbers.
             write!(f, "{}", x)
         } else {
             write_hex(x, f)
         }
     }
 }
 
-/// Parse a 64-bit number.
-fn parse_i64(s: &str) -> Result<i64, &'static str> {
+/// Parse a 64-bit unsigned number.
+fn parse_u64(s: &str) -> Result<u64, &'static str> {
     let mut value: u64 = 0;
     let mut digits = 0;
-    let negative = s.starts_with('-');
-    let s2 = if negative || s.starts_with('+') {
-        &s[1..]
-    } else {
-        s
-    };
 
-    if s2.starts_with("0x") {
+    if s.starts_with("-0x") {
+        return Err("Invalid character in hexadecimal number");
+    } else if s.starts_with("0x") {
         // Hexadecimal.
-        for ch in s2[2..].chars() {
+        for ch in s[2..].chars() {
             match ch.to_digit(16) {
                 Some(digit) => {
                     digits += 1;
                     if digits > 16 {
                         return Err("Too many hexadecimal digits");
                     }
                     // This can't overflow given the digit limit.
                     value = (value << 4) | u64::from(digit);
@@ -96,17 +166,17 @@ fn parse_i64(s: &str) -> Result<i64, &'s
                     if ch != '_' {
                         return Err("Invalid character in hexadecimal number");
                     }
                 }
             }
         }
     } else {
         // Decimal number, possibly negative.
-        for ch in s2.chars() {
+        for ch in s.chars() {
             match ch.to_digit(16) {
                 Some(digit) => {
                     digits += 1;
                     match value.checked_mul(10) {
                         None => return Err("Too large decimal number"),
                         Some(v) => value = v,
                     }
                     match value.checked_add(u64::from(digit)) {
@@ -123,33 +193,25 @@ fn parse_i64(s: &str) -> Result<i64, &'s
             }
         }
     }
 
     if digits == 0 {
         return Err("No digits in number");
     }
 
-    // We support the range-and-a-half from -2^63 .. 2^64-1.
-    if negative {
-        value = value.wrapping_neg();
-        // Don't allow large negative values to wrap around and become positive.
-        if value as i64 > 0 {
-            return Err("Negative number too small");
-        }
-    }
-    Ok(value as i64)
+    Ok(value)
 }
 
-impl FromStr for Imm64 {
+impl FromStr for Uimm64 {
     type Err = &'static str;
 
-    // Parse a decimal or hexadecimal `Imm64`, formatted as above.
+    // Parse a decimal or hexadecimal `Uimm64`, formatted as above.
     fn from_str(s: &str) -> Result<Self, &'static str> {
-        parse_i64(s).map(Self::new)
+        parse_u64(s).map(Self::new)
     }
 }
 
 /// 8-bit unsigned integer immediate operand.
 ///
 /// This is used to indicate lane indexes typically.
 pub type Uimm8 = u8;
 
@@ -177,17 +239,17 @@ impl From<u32> for Uimm32 {
     }
 }
 
 impl Display for Uimm32 {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         if self.0 < 10_000 {
             write!(f, "{}", self.0)
         } else {
-            write_hex(i64::from(self.0), f)
+            write_hex(u64::from(self.0), f)
         }
     }
 }
 
 impl FromStr for Uimm32 {
     type Err = &'static str;
 
     // Parse a decimal or hexadecimal `Uimm32`, formatted as above.
@@ -263,17 +325,17 @@ impl Display for Offset32 {
 
         // Always include a sign.
         write!(f, "{}", if self.0 < 0 { '-' } else { '+' })?;
 
         let val = i64::from(self.0).abs();
         if val < 10_000 {
             write!(f, "{}", val)
         } else {
-            write_hex(val, f)
+            write_hex(val as u64, f)
         }
     }
 }
 
 impl FromStr for Offset32 {
     type Err = &'static str;
 
     // Parse a decimal or hexadecimal `Offset32`, formatted as above.
@@ -678,16 +740,30 @@ mod tests {
         assert_eq!(Imm64(9999).to_string(), "9999");
         assert_eq!(Imm64(10000).to_string(), "0x2710");
         assert_eq!(Imm64(-9999).to_string(), "-9999");
         assert_eq!(Imm64(-10000).to_string(), "0xffff_ffff_ffff_d8f0");
         assert_eq!(Imm64(0xffff).to_string(), "0xffff");
         assert_eq!(Imm64(0x10000).to_string(), "0x0001_0000");
     }
 
+    #[test]
+    fn format_uimm64() {
+        assert_eq!(Uimm64(0).to_string(), "0");
+        assert_eq!(Uimm64(9999).to_string(), "9999");
+        assert_eq!(Uimm64(10000).to_string(), "0x2710");
+        assert_eq!(Uimm64(-9999i64 as u64).to_string(), "0xffff_ffff_ffff_d8f1");
+        assert_eq!(
+            Uimm64(-10000i64 as u64).to_string(),
+            "0xffff_ffff_ffff_d8f0"
+        );
+        assert_eq!(Uimm64(0xffff).to_string(), "0xffff");
+        assert_eq!(Uimm64(0x10000).to_string(), "0x0001_0000");
+    }
+
     // Verify that `text` can be parsed as a `T` into a value that displays as `want`.
     fn parse_ok<T: FromStr + Display>(text: &str, want: &str)
     where
         <T as FromStr>::Err: Display,
     {
         match text.parse::<T>() {
             Err(s) => panic!("\"{}\".parse() error: {}", text, s),
             Ok(x) => assert_eq!(x.to_string(), want),
@@ -746,16 +822,56 @@ mod tests {
         parse_err::<Imm64>("--", "Invalid character in decimal number");
         parse_err::<Imm64>("-0x-", "Invalid character in hexadecimal number");
 
         // Hex count overflow.
         parse_err::<Imm64>("0x0_0000_0000_0000_0000", "Too many hexadecimal digits");
     }
 
     #[test]
+    fn parse_uimm64() {
+        parse_ok::<Uimm64>("0", "0");
+        parse_ok::<Uimm64>("1", "1");
+        parse_ok::<Uimm64>("0x0", "0");
+        parse_ok::<Uimm64>("0xf", "15");
+        parse_ok::<Uimm64>("0xffffffff_fffffff7", "0xffff_ffff_ffff_fff7");
+
+        // Probe limits.
+        parse_ok::<Uimm64>("0xffffffff_ffffffff", "0xffff_ffff_ffff_ffff");
+        parse_ok::<Uimm64>("0x80000000_00000000", "0x8000_0000_0000_0000");
+        parse_ok::<Uimm64>("18446744073709551615", "0xffff_ffff_ffff_ffff");
+        // Overflow both the `checked_add` and `checked_mul`.
+        parse_err::<Uimm64>("18446744073709551616", "Too large decimal number");
+        parse_err::<Uimm64>("184467440737095516100", "Too large decimal number");
+
+        // Underscores are allowed where digits go.
+        parse_ok::<Uimm64>("0_0", "0");
+        parse_ok::<Uimm64>("_10_", "10");
+        parse_ok::<Uimm64>("0x97_88_bb", "0x0097_88bb");
+        parse_ok::<Uimm64>("0x_97_", "151");
+
+        parse_err::<Uimm64>("", "No digits in number");
+        parse_err::<Uimm64>("_", "No digits in number");
+        parse_err::<Uimm64>("0x", "No digits in number");
+        parse_err::<Uimm64>("0x_", "No digits in number");
+        parse_err::<Uimm64>("-", "Invalid character in decimal number");
+        parse_err::<Uimm64>("-0x", "Invalid character in hexadecimal number");
+        parse_err::<Uimm64>(" ", "Invalid character in decimal number");
+        parse_err::<Uimm64>("0 ", "Invalid character in decimal number");
+        parse_err::<Uimm64>(" 0", "Invalid character in decimal number");
+        parse_err::<Uimm64>("--", "Invalid character in decimal number");
+        parse_err::<Uimm64>("-0x-", "Invalid character in hexadecimal number");
+        parse_err::<Uimm64>("-0", "Invalid character in decimal number");
+        parse_err::<Uimm64>("-1", "Invalid character in decimal number");
+
+        // Hex count overflow.
+        parse_err::<Uimm64>("0x0_0000_0000_0000_0000", "Too many hexadecimal digits");
+    }
+
+    #[test]
     fn format_offset32() {
         assert_eq!(Offset32(0).to_string(), "");
         assert_eq!(Offset32(1).to_string(), "+1");
         assert_eq!(Offset32(-1).to_string(), "-1");
         assert_eq!(Offset32(9999).to_string(), "+9999");
         assert_eq!(Offset32(10000).to_string(), "+0x2710");
         assert_eq!(Offset32(-9999).to_string(), "-9999");
         assert_eq!(Offset32(-10000).to_string(), "-0x2710");
--- a/third_party/rust/cranelift-codegen/src/ir/memflags.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/memflags.rs
@@ -21,16 +21,25 @@ pub struct MemFlags {
 }
 
 impl MemFlags {
     /// Create a new empty set of flags.
     pub fn new() -> Self {
         Self { bits: 0 }
     }
 
+    /// Create a set of flags representing an access from a "trusted" address, meaning it's
+    /// known to be aligned and non-trapping.
+    pub fn trusted() -> Self {
+        let mut result = Self::new();
+        result.set_notrap();
+        result.set_aligned();
+        result
+    }
+
     /// Read a flag bit.
     fn read(self, bit: FlagBit) -> bool {
         self.bits & (1 << bit as usize) != 0
     }
 
     /// Set a flag bit.
     fn set(&mut self, bit: FlagBit) {
         self.bits |= 1 << bit as usize
--- a/third_party/rust/cranelift-codegen/src/ir/progpoint.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/progpoint.rs
@@ -80,19 +80,19 @@ impl From<ValueDef> for ExpandedProgramP
             ValueDef::Param(ebb, _) => ebb.into(),
         }
     }
 }
 
 impl From<ProgramPoint> for ExpandedProgramPoint {
     fn from(pp: ProgramPoint) -> Self {
         if pp.0 & 1 == 0 {
-            ExpandedProgramPoint::Inst(Inst::new((pp.0 / 2) as usize))
+            ExpandedProgramPoint::Inst(Inst::from_u32(pp.0 / 2))
         } else {
-            ExpandedProgramPoint::Ebb(Ebb::new((pp.0 / 2) as usize))
+            ExpandedProgramPoint::Ebb(Ebb::from_u32(pp.0 / 2))
         }
     }
 }
 
 impl fmt::Display for ExpandedProgramPoint {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             ExpandedProgramPoint::Inst(x) => write!(f, "{}", x),
--- a/third_party/rust/cranelift-codegen/src/ir/table.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/table.rs
@@ -1,29 +1,29 @@
 //! Tables.
 
-use ir::immediates::Imm64;
+use ir::immediates::Uimm64;
 use ir::{GlobalValue, Type};
 use std::fmt;
 
 /// Information about a table declaration.
 #[derive(Clone)]
 pub struct TableData {
     /// Global value giving the address of the start of the table.
     pub base_gv: GlobalValue,
 
     /// Guaranteed minimum table size in elements. Table accesses before `min_size` don't need
     /// bounds checking.
-    pub min_size: Imm64,
+    pub min_size: Uimm64,
 
     /// Global value giving the current bound of the table, in elements.
     pub bound_gv: GlobalValue,
 
     /// The size of a table element, in bytes.
-    pub element_size: Imm64,
+    pub element_size: Uimm64,
 
     /// The index type for the table.
     pub index_type: Type,
 }
 
 impl fmt::Display for TableData {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str("dynamic")?;
--- a/third_party/rust/cranelift-codegen/src/ir/trapcode.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/trapcode.rs
@@ -12,17 +12,18 @@ pub enum TrapCode {
     ///
     /// On some platforms, a stack overflow may also be indicated by a segmentation fault from the
     /// stack guard page.
     StackOverflow,
 
     /// A `heap_addr` instruction detected an out-of-bounds error.
     ///
     /// Note that not all out-of-bounds heap accesses are reported this way;
-    /// some are detected by a segmentation fault on the heap guard pages.
+    /// some are detected by a segmentation fault on the heap unmapped or
+    /// offset-guard pages.
     HeapOutOfBounds,
 
     /// A `table_addr` instruction detected an out-of-bounds error.
     TableOutOfBounds,
 
     /// Other bounds checking error.
     OutOfBounds,
 
--- a/third_party/rust/cranelift-codegen/src/ir/types.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/types.rs
@@ -1,12 +1,13 @@
 //! Common types for the Cranelift code generator.
 
 use std::default::Default;
 use std::fmt::{self, Debug, Display, Formatter};
+use target_lexicon::{PointerWidth, Triple};
 
 /// The type of an SSA value.
 ///
 /// The `INVALID` type isn't a real type, and is used as a placeholder in the IR where a type
 /// field is present put no type is needed, such as the controlling type variable for a
 /// non-polymorphic instruction.
 ///
 /// Basic integer types: `I8`, `I16`, `I32`, and `I64`. These types are sign-agnostic.
@@ -266,16 +267,26 @@ impl Type {
 
     /// True iff:
     ///
     /// 1. `self.lane_count() == other.lane_count()` and
     /// 2. `self.lane_bits() >= other.lane_bits()`
     pub fn wider_or_equal(self, other: Self) -> bool {
         self.lane_count() == other.lane_count() && self.lane_bits() >= other.lane_bits()
     }
+
+    /// Return the pointer type for the given target triple.
+    pub fn triple_pointer_type(triple: &Triple) -> Self {
+        match triple.pointer_width() {
+            Ok(PointerWidth::U16) => I16,
+            Ok(PointerWidth::U32) => I32,
+            Ok(PointerWidth::U64) => I64,
+            Err(()) => panic!("unable to determine architecture pointer width"),
+        }
+    }
 }
 
 impl Display for Type {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         if self.is_bool() {
             write!(f, "b{}", self.lane_bits())
         } else if self.is_int() {
             write!(f, "i{}", self.lane_bits())
--- a/third_party/rust/cranelift-codegen/src/isa/call_conv.rs
+++ b/third_party/rust/cranelift-codegen/src/isa/call_conv.rs
@@ -16,17 +16,17 @@ pub enum CallConv {
     /// SpiderMonkey WebAssembly convention
     Baldrdash,
     /// Specialized convention for the probestack function
     Probestack,
 }
 
 impl CallConv {
     /// Return the default calling convention for the given target triple.
-    pub fn default_for_triple(triple: &Triple) -> Self {
+    pub fn triple_default(triple: &Triple) -> Self {
         match triple.default_calling_convention() {
             // Default to System V for unknown targets because most everything
             // uses System V.
             Ok(CallingConvention::SystemV) | Err(()) => CallConv::SystemV,
             Ok(CallingConvention::WindowsFastcall) => CallConv::WindowsFastcall,
         }
     }
 }
--- a/third_party/rust/cranelift-codegen/src/isa/mod.rs
+++ b/third_party/rust/cranelift-codegen/src/isa/mod.rs
@@ -203,17 +203,17 @@ pub trait TargetIsa: fmt::Display + Sync
     /// Get the target triple that was used to make this trait object.
     fn triple(&self) -> &Triple;
 
     /// Get the ISA-independent flags that were used to make this trait object.
     fn flags(&self) -> &settings::Flags;
 
     /// Get the default calling convention of this target.
     fn default_call_conv(&self) -> CallConv {
-        CallConv::default_for_triple(self.triple())
+        CallConv::triple_default(self.triple())
     }
 
     /// Get the pointer type of this ISA.
     fn pointer_type(&self) -> ir::Type {
         ir::Type::int(u16::from(self.pointer_bits())).unwrap()
     }
 
     /// Get the width of pointers on this ISA.
--- a/third_party/rust/cranelift-codegen/src/isa/registers.rs
+++ b/third_party/rust/cranelift-codegen/src/isa/registers.rs
@@ -84,17 +84,18 @@ impl RegBank {
             None => {
                 // Try a regular prefixed name.
                 if name.starts_with(self.prefix) {
                     name[self.prefix.len()..].parse().ok()
                 } else {
                     None
                 }
             }
-        }.and_then(|offset| {
+        }
+        .and_then(|offset| {
             if offset < self.units {
                 Some(offset + self.first_unit)
             } else {
                 None
             }
         })
     }
 
--- a/third_party/rust/cranelift-codegen/src/isa/x86/abi.rs
+++ b/third_party/rust/cranelift-codegen/src/isa/x86/abi.rs
@@ -93,17 +93,18 @@ impl ArgAssigner for Args {
         if ty.is_int() && self.call_conv == CallConv::Baldrdash {
             match arg.purpose {
                 // This is SpiderMonkey's `WasmTlsReg`.
                 ArgumentPurpose::VMContext => {
                     return ArgumentLoc::Reg(if self.pointer_bits == 64 {
                         RU::r14
                     } else {
                         RU::rsi
-                    } as RegUnit).into()
+                    } as RegUnit)
+                    .into()
                 }
                 // This is SpiderMonkey's `WasmTableCallSigReg`.
                 ArgumentPurpose::SignatureId => return ArgumentLoc::Reg(RU::r10 as RegUnit).into(),
                 _ => {}
             }
         }
 
         // Try to use a GPR.
--- a/third_party/rust/cranelift-codegen/src/legalizer/boundary.rs
+++ b/third_party/rust/cranelift-codegen/src/legalizer/boundary.rs
@@ -570,17 +570,18 @@ pub fn handle_return_abi(inst: Inst, fun
         .signature
         .returns
         .iter()
         .rev()
         .take_while(|&rt| {
             rt.purpose == ArgumentPurpose::Link
                 || rt.purpose == ArgumentPurpose::StructReturn
                 || rt.purpose == ArgumentPurpose::VMContext
-        }).count();
+        })
+        .count();
     let abi_args = func.signature.returns.len() - special_args;
 
     let pos = &mut FuncCursor::new(func).at_inst(inst);
     pos.use_srcloc(inst);
 
     legalize_inst_arguments(pos, cfg, abi_args, |func, abi_arg| {
         func.signature.returns[abi_arg]
     });
@@ -689,17 +690,18 @@ fn spill_call_arguments(pos: &mut FuncCu
                         if locations[arg] != ValueLoc::Stack(ss) {
                             Some((idx, arg, ss))
                         } else {
                             None
                         }
                     }
                     _ => None,
                 }
-            }).collect::<Vec<_>>()
+            })
+            .collect::<Vec<_>>()
     };
 
     if arglist.is_empty() {
         return false;
     }
 
     // Insert the spill instructions and rewrite call arguments.
     for (idx, arg, ss) in arglist {
--- a/third_party/rust/cranelift-codegen/src/legalizer/globalvalue.rs
+++ b/third_party/rust/cranelift-codegen/src/legalizer/globalvalue.rs
@@ -105,19 +105,17 @@ fn load_addr(
         pos.func
             .special_param(ir::ArgumentPurpose::VMContext)
             .expect("Missing vmctx parameter")
     } else {
         pos.ins().global_value(ptr_ty, base)
     };
 
     // Global-value loads are always notrap and aligned. They may be readonly.
-    let mut mflags = ir::MemFlags::new();
-    mflags.set_notrap();
-    mflags.set_aligned();
+    let mut mflags = ir::MemFlags::trusted();
     if readonly {
         mflags.set_readonly();
     }
 
     // Perform the load.
     pos.func
         .dfg
         .replace(inst)
--- a/third_party/rust/cranelift-codegen/src/legalizer/heap.rs
+++ b/third_party/rust/cranelift-codegen/src/legalizer/heap.rs
@@ -44,17 +44,17 @@ pub fn expand_heap_addr(
 fn dynamic_addr(
     inst: ir::Inst,
     heap: ir::Heap,
     offset: ir::Value,
     access_size: u32,
     bound_gv: ir::GlobalValue,
     func: &mut ir::Function,
 ) {
-    let access_size = i64::from(access_size);
+    let access_size = u64::from(access_size);
     let offset_ty = func.dfg.value_type(offset);
     let addr_ty = func.dfg.value_type(func.dfg.first_result(inst));
     let min_size = func.heaps[heap].min_size.into();
     let mut pos = FuncCursor::new(func).at_inst(inst);
     pos.use_srcloc(inst);
 
     // Start with the bounds check. Trap if `offset + access_size > bound`.
     let bound = pos.ins().global_value(offset_ty, bound_gv);
@@ -62,23 +62,23 @@ fn dynamic_addr(
     if access_size == 1 {
         // `offset > bound - 1` is the same as `offset >= bound`.
         oob = pos
             .ins()
             .icmp(IntCC::UnsignedGreaterThanOrEqual, offset, bound);
     } else if access_size <= min_size {
         // We know that bound >= min_size, so here we can compare `offset > bound - access_size`
         // without wrapping.
-        let adj_bound = pos.ins().iadd_imm(bound, -access_size);
+        let adj_bound = pos.ins().iadd_imm(bound, -(access_size as i64));
         oob = pos
             .ins()
             .icmp(IntCC::UnsignedGreaterThan, offset, adj_bound);
     } else {
         // We need an overflow check for the adjusted offset.
-        let access_size_val = pos.ins().iconst(offset_ty, access_size);
+        let access_size_val = pos.ins().iconst(offset_ty, access_size as i64);
         let (adj_offset, overflow) = pos.ins().iadd_cout(offset, access_size_val);
         pos.ins().trapnz(overflow, ir::TrapCode::HeapOutOfBounds);
         oob = pos
             .ins()
             .icmp(IntCC::UnsignedGreaterThan, adj_offset, bound);
     }
     pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
 
@@ -86,21 +86,21 @@ fn dynamic_addr(
 }
 
 /// Expand a `heap_addr` for a static heap.
 fn static_addr(
     inst: ir::Inst,
     heap: ir::Heap,
     offset: ir::Value,
     access_size: u32,
-    bound: i64,
+    bound: u64,
     func: &mut ir::Function,
     cfg: &mut ControlFlowGraph,
 ) {
-    let access_size = i64::from(access_size);
+    let access_size = u64::from(access_size);
     let offset_ty = func.dfg.value_type(offset);
     let addr_ty = func.dfg.value_type(func.dfg.first_result(inst));
     let mut pos = FuncCursor::new(func).at_inst(inst);
     pos.use_srcloc(inst);
 
     // Start with the bounds check. Trap if `offset + access_size > bound`.
     if access_size > bound {
         // This will simply always trap since `offset >= 0`.
@@ -112,29 +112,29 @@ fn static_addr(
         let new_ebb = pos.func.dfg.make_ebb();
         pos.insert_ebb(new_ebb);
         cfg.recompute_ebb(pos.func, curr_ebb);
         cfg.recompute_ebb(pos.func, new_ebb);
         return;
     }
 
     // Check `offset > limit` which is now known non-negative.
-    let limit = bound - access_size;
+    let limit = bound - u64::from(access_size);
 
     // We may be able to omit the check entirely for 32-bit offsets if the heap bound is 4 GB or
     // more.
     if offset_ty != ir::types::I32 || limit < 0xffff_ffff {
         let oob = if limit & 1 == 1 {
             // Prefer testing `offset >= limit - 1` when limit is odd because an even number is
             // likely to be a convenient constant on ARM and other RISC architectures.
             pos.ins()
-                .icmp_imm(IntCC::UnsignedGreaterThanOrEqual, offset, limit - 1)
+                .icmp_imm(IntCC::UnsignedGreaterThanOrEqual, offset, limit as i64 - 1)
         } else {
             pos.ins()
-                .icmp_imm(IntCC::UnsignedGreaterThan, offset, limit)
+                .icmp_imm(IntCC::UnsignedGreaterThan, offset, limit as i64)
         };
         pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
     }
 
     compute_addr(inst, heap, addr_ty, offset, offset_ty, pos.func);
 }
 
 /// Emit code for the base address computation of a `heap_addr` instruction.
--- a/third_party/rust/cranelift-codegen/src/legalizer/mod.rs
+++ b/third_party/rust/cranelift-codegen/src/legalizer/mod.rs
@@ -395,20 +395,18 @@ fn expand_stack_load(
         _ => panic!(
             "Expected stack_load: {}",
             pos.func.dfg.display_inst(inst, None)
         ),
     };
 
     let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);
 
-    let mut mflags = MemFlags::new();
     // Stack slots are required to be accessible and aligned.
-    mflags.set_notrap();
-    mflags.set_aligned();
+    let mflags = MemFlags::trusted();
     pos.func.dfg.replace(inst).load(ty, mflags, addr, 0);
 }
 
 /// Expand illegal `stack_store` instructions.
 fn expand_stack_store(
     inst: ir::Inst,
     func: &mut ir::Function,
     _cfg: &mut ControlFlowGraph,
--- a/third_party/rust/cranelift-codegen/src/legalizer/split.rs
+++ b/third_party/rust/cranelift-codegen/src/legalizer/split.rs
@@ -160,17 +160,18 @@ fn split_any(
 
             // The `hi` part goes at the end. Since multiple repairs may have been scheduled to the
             // same EBB, there could be multiple arguments missing.
             if num_args > num_fixed_args + repair.hi_num {
                 *args
                     .get_mut(
                         num_fixed_args + repair.hi_num,
                         &mut pos.func.dfg.value_lists,
-                    ).unwrap() = hi;
+                    )
+                    .unwrap() = hi;
             } else {
                 // We need to append one or more arguments. If we're adding more than one argument,
                 // there must be pending repairs on the stack that will fill in the correct values
                 // instead of `hi`.
                 args.extend(
                     iter::repeat(hi).take(1 + num_fixed_args + repair.hi_num - num_args),
                     &mut pos.func.dfg.value_lists,
                 );
--- a/third_party/rust/cranelift-codegen/src/legalizer/table.rs
+++ b/third_party/rust/cranelift-codegen/src/legalizer/table.rs
@@ -87,27 +87,25 @@ fn compute_addr(
     }
 
     // Add the table base address base
     let base_gv = pos.func.tables[table].base_gv;
     let base = pos.ins().global_value(addr_ty, base_gv);
 
     let element_size = pos.func.tables[table].element_size;
     let mut offset;
-    let element_size_i64: i64 = element_size.into();
-    debug_assert!(element_size_i64 >= 0);
-    let element_size_u64 = element_size_i64 as u64;
-    if element_size_u64 == 1 {
+    let element_size: u64 = element_size.into();
+    if element_size == 1 {
         offset = index;
-    } else if element_size_u64.is_power_of_two() {
+    } else if element_size.is_power_of_two() {
         offset = pos
             .ins()
-            .ishl_imm(index, i64::from(element_size_u64.trailing_zeros()));
+            .ishl_imm(index, i64::from(element_size.trailing_zeros()));
     } else {
-        offset = pos.ins().imul_imm(index, element_size);
+        offset = pos.ins().imul_imm(index, element_size as i64);
     }
 
     if element_offset == Offset32::new(0) {
         pos.func.dfg.replace(inst).iadd(base, offset);
     } else {
         let imm: i64 = element_offset.into();
         offset = pos.ins().iadd(base, offset);
         pos.func.dfg.replace(inst).iadd_imm(offset, imm);
--- a/third_party/rust/cranelift-codegen/src/lib.rs
+++ b/third_party/rust/cranelift-codegen/src/lib.rs
@@ -1,17 +1,14 @@
 //! Cranelift code generation library.
 
 #![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
 #![warn(unused_import_braces)]
 #![cfg_attr(feature = "std", deny(unstable_features))]
-#![cfg_attr(
-    feature = "clippy",
-    plugin(clippy(conf_file = "../../clippy.toml"))
-)]
+#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
 #![cfg_attr(feature="cargo-clippy", allow(
 // Produces only a false positive:
                 while_let_loop,
 // Produces many false positives, but did produce some valid lints, now fixed:
                 needless_lifetimes,
 // Generated code makes some style transgressions, but readability doesn't suffer much:
                 many_single_char_names,
                 identity_op,
@@ -28,24 +25,24 @@
 // These are relatively minor style issues, but would be easy to fix:
                 new_without_default,
                 new_without_default_derive,
                 should_implement_trait,
                 len_without_is_empty))]
 #![cfg_attr(
     feature = "cargo-clippy",
     warn(
-        float_arithmetic,
-        mut_mut,
-        nonminimal_bool,
-        option_map_unwrap_or,
-        option_map_unwrap_or_else,
-        print_stdout,
-        unicode_not_nfc,
-        use_self
+        clippy::float_arithmetic,
+        clippy::mut_mut,
+        clippy::nonminimal_bool,
+        clippy::option_map_unwrap_or,
+        clippy::option_map_unwrap_or_else,
+        clippy::print_stdout,
+        clippy::unicode_not_nfc,
+        clippy::use_self
     )
 )]
 // Turns on no_std and alloc features if std is not available.
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(not(feature = "std"), feature(alloc))]
 // TODO: Remove this workaround once https://github.com/rust-lang/rust/issues/27747 is done.
 #![cfg_attr(not(feature = "std"), feature(slice_concat_ext))]
 
--- a/third_party/rust/cranelift-codegen/src/print_errors.rs
+++ b/third_party/rust/cranelift-codegen/src/print_errors.rs
@@ -25,24 +25,26 @@ pub fn pretty_verifier_error<'a>(
     let mut w = String::new();
     let num_errors = errors.len();
 
     decorate_function(
         &mut PrettyVerifierError(func_w.unwrap_or_else(|| Box::new(PlainWriter)), &mut errors),
         &mut w,
         func,
         isa,
-    ).unwrap();
+    )
+    .unwrap();
 
     writeln!(
         w,
         "\n; {} verifier error{} detected (see above). Compilation aborted.",
         num_errors,
         if num_errors == 1 { "" } else { "s" }
-    ).unwrap();
+    )
+    .unwrap();
 
     w
 }
 
 struct PrettyVerifierError<'a>(Box<FuncWriter + 'a>, &'a mut Vec<VerifierError>);
 
 impl<'a> FuncWriter for PrettyVerifierError<'a> {
     fn write_ebb_header(
--- a/third_party/rust/cranelift-codegen/src/regalloc/coloring.rs
+++ b/third_party/rust/cranelift-codegen/src/regalloc/coloring.rs
@@ -909,22 +909,20 @@ impl<'a> Context<'a> {
             NotABranch => false,
             SingleDest(ebb, _) => {
                 let lr = &self.liveness[value];
                 lr.is_livein(ebb, ctx)
             }
             Table(jt, ebb) => {
                 let lr = &self.liveness[value];
                 !lr.is_local()
-                    && (ebb.map_or(false, |ebb| lr.is_livein(ebb, ctx)) || self
-                        .cur
-                        .func
-                        .jump_tables[jt]
-                        .iter()
-                        .any(|ebb| lr.is_livein(*ebb, ctx)))
+                    && (ebb.map_or(false, |ebb| lr.is_livein(ebb, ctx))
+                        || self.cur.func.jump_tables[jt]
+                            .iter()
+                            .any(|ebb| lr.is_livein(*ebb, ctx)))
             }
         }
     }
 
     /// Emit `regmove` instructions as needed to move the live registers into place before the
     /// instruction. Also update `self.divert` accordingly.
     ///
     /// The `self.cur` cursor is expected to point at the instruction. The register moves are
--- a/third_party/rust/cranelift-codegen/src/regalloc/context.rs
+++ b/third_party/rust/cranelift-codegen/src/regalloc/context.rs
@@ -111,17 +111,18 @@ impl Context {
                 && verify_liveness(isa, func, cfg, &self.liveness, &mut errors).is_ok()
                 && verify_cssa(
                     func,
                     cfg,
                     domtree,
                     &self.liveness,
                     &self.virtregs,
                     &mut errors,
-                ).is_ok();
+                )
+                .is_ok();
 
             if !ok {
                 return Err(errors.into());
             }
         }
 
         // Pass: Spilling.
         self.spilling.run(
@@ -139,17 +140,18 @@ impl Context {
                 && verify_liveness(isa, func, cfg, &self.liveness, &mut errors).is_ok()
                 && verify_cssa(
                     func,
                     cfg,
                     domtree,
                     &self.liveness,
                     &self.virtregs,
                     &mut errors,
-                ).is_ok();
+                )
+                .is_ok();
 
             if !ok {
                 return Err(errors.into());
             }
         }
 
         // Pass: Reload.
         self.reload.run(
@@ -166,17 +168,18 @@ impl Context {
                 && verify_liveness(isa, func, cfg, &self.liveness, &mut errors).is_ok()
                 && verify_cssa(
                     func,
                     cfg,
                     domtree,
                     &self.liveness,
                     &self.virtregs,
                     &mut errors,
-                ).is_ok();
+                )
+                .is_ok();
 
             if !ok {
                 return Err(errors.into());
             }
         }
 
         // Pass: Coloring.
         self.coloring
@@ -188,17 +191,18 @@ impl Context {
                 && verify_locations(isa, func, Some(&self.liveness), &mut errors).is_ok()
                 && verify_cssa(
                     func,
                     cfg,
                     domtree,
                     &self.liveness,
                     &self.virtregs,
                     &mut errors,
-                ).is_ok();
+                )
+                .is_ok();
 
             if !ok {
                 return Err(errors.into());
             }
         }
 
         // Even if we arrive here, (non-fatal) errors might have been reported, so we
         // must make sure absolutely nothing is wrong
--- a/third_party/rust/cranelift-codegen/src/regalloc/liveness.rs
+++ b/third_party/rust/cranelift-codegen/src/regalloc/liveness.rs
@@ -215,17 +215,18 @@ fn get_or_create<'a>(
                     .operand_constraints(func.encodings[inst])
                     .and_then(|rc| rc.outs.get(rnum))
                     .map(Affinity::new)
                     .or_else(|| {
                         // If this is a call, get the return value affinity.
                         func.dfg
                             .call_signature(inst)
                             .map(|sig| Affinity::abi(&func.dfg.signatures[sig].returns[rnum], isa))
-                    }).unwrap_or_default();
+                    })
+                    .unwrap_or_default();
             }
             ValueDef::Param(ebb, num) => {
                 def = ebb.into();
                 if func.layout.entry_block() == Some(ebb) {
                     // The affinity for entry block parameters can be inferred from the function
                     // signature.
                     affinity = Affinity::abi(&func.signature.params[num], isa);
                 } else {
--- a/third_party/rust/cranelift-codegen/src/regalloc/register_set.rs
+++ b/third_party/rust/cranelift-codegen/src/regalloc/register_set.rs
@@ -202,17 +202,18 @@ impl<'a> fmt::Display for DisplayRegiste
                                 f,
                                 "{}",
                                 bank.names
                                     .get(offset as usize)
                                     .and_then(|name| name.chars().nth(1))
                                     .unwrap_or_else(|| char::from_digit(
                                         u32::from(offset % 10),
                                         10
-                                    ).unwrap())
+                                    )
+                                    .unwrap())
                             )?;
                         }
                     }
                 }
             }
         }
         write!(f, " ]")
     }
--- a/third_party/rust/cranelift-codegen/src/regalloc/reload.rs
+++ b/third_party/rust/cranelift-codegen/src/regalloc/reload.rs
@@ -259,19 +259,19 @@ impl<'a> Context<'a> {
                         self.insert_spill(ebb, lv.value, reg);
                     }
                 }
             }
         }
 
         // Same thing for spilled call return values.
         let retvals = &defs[self.cur.func.dfg[inst]
-                                .opcode()
-                                .constraints()
-                                .num_fixed_results()..];
+            .opcode()
+            .constraints()
+            .num_fixed_results()..];
         if !retvals.is_empty() {
             let sig = self
                 .cur
                 .func
                 .dfg
                 .call_signature(inst)
                 .expect("Extra results on non-call instruction");
             for (i, lv) in retvals.iter().enumerate() {
--- a/third_party/rust/cranelift-codegen/src/regalloc/spilling.rs
+++ b/third_party/rust/cranelift-codegen/src/regalloc/spilling.rs
@@ -499,17 +499,18 @@ impl<'a> Context<'a> {
                 if let Affinity::Reg(rci) = lv.affinity {
                     let rc = self.reginfo.rc(rci);
                     if (mask & (1 << rc.toprc)) != 0 && !self.spills.contains(&lv.value) {
                         // Here, `lv` is a viable spill candidate.
                         return Some(lv.value);
                     }
                 }
                 None
-            }).min_by(|&a, &b| {
+            })
+            .min_by(|&a, &b| {
                 // Find the minimum candidate according to the RPO of their defs.
                 self.domtree.rpo_cmp(
                     self.cur.func.dfg.value_def(a),
                     self.cur.func.dfg.value_def(b),
                     &self.cur.func.layout,
                 )
             })
     }
--- a/third_party/rust/cranelift-codegen/src/regalloc/virtregs.rs
+++ b/third_party/rust/cranelift-codegen/src/regalloc/virtregs.rs
@@ -8,17 +8,16 @@
 //! that is, one value is passed as an EBB argument to a branch and the other is the EBB parameter
 //! value itself.
 //!
 //! If any values in a virtual register are spilled, they will use the same stack slot. This avoids
 //! memory-to-memory copies when a spilled value is passed as an EBB argument.
 
 use dbg::DisplayList;
 use dominator_tree::DominatorTreePreorder;
-use entity::EntityRef;
 use entity::{EntityList, ListPool};
 use entity::{Keys, PrimaryMap, SecondaryMap};
 use ir::{Function, Value};
 use packed_option::PackedOption;
 use ref_slice::ref_slice;
 use std::cmp::Ordering;
 use std::fmt;
 use std::vec::Vec;
@@ -253,25 +252,25 @@ enum UFEntry {
 /// The rank of a set is an upper bound on the number of links that must be followed from a member
 /// of the set to the set leader.
 ///
 /// A singleton set is the same as a set with rank 0. It contains only the leader value.
 impl UFEntry {
     /// Decode a table entry.
     fn decode(x: i32) -> Self {
         if x < 0 {
-            UFEntry::Link(Value::new((!x) as usize))
+            UFEntry::Link(Value::from_u32((!x) as u32))
         } else {
             UFEntry::Rank(x as u32)
         }
     }
 
     /// Encode a link entry.
     fn encode_link(v: Value) -> i32 {
-        !(v.index() as i32)
+        !(v.as_u32() as i32)
     }
 }
 
 /// Union-find algorithm for building virtual registers.
 ///
 /// Before values are added to virtual registers, it is possible to use a union-find algorithm to
 /// construct virtual registers efficiently. This support implemented here is used as follows:
 ///
--- a/third_party/rust/cranelift-codegen/src/timing.rs
+++ b/third_party/rust/cranelift-codegen/src/timing.rs
@@ -32,17 +32,17 @@ macro_rules! define_passes {
             pub fn $pass() -> TimingToken {
                 details::start_pass($enum::$pass)
             }
         )+
     }
 }
 
 // Pass definitions.
-define_passes!{
+define_passes! {
     Pass, NUM_PASSES, DESCRIPTIONS;
 
     process_file: "Processing test file",
     parse_text: "Parsing textual Cranelift IR",
     wasm_translate_module: "Translate WASM module",
     wasm_translate_function: "Translate WASM function",
 
     verifier: "Verify Cranelift IR",
@@ -171,17 +171,17 @@ mod details {
                 }
                 writeln!(f, " {}", desc)?;
             }
             writeln!(f, "======== ========  ==================================")
         }
     }
 
     /// Information about passes in a single thread.
-    thread_local!{
+    thread_local! {
         static CURRENT_PASS: Cell<Pass> = Cell::new(Pass::None);
         static PASS_TIME: RefCell<PassTimes> = RefCell::new(Default::default());
     }
 
     /// Start timing `pass` as a child of the currently running pass, if any.
     ///
     /// This function is called by the publicly exposed pass functions.
     pub(super) fn start_pass(pass: Pass) -> TimingToken {
--- a/third_party/rust/cranelift-codegen/src/verifier/mod.rs
+++ b/third_party/rust/cranelift-codegen/src/verifier/mod.rs
@@ -882,21 +882,21 @@ impl<'a> Verifier<'a> {
                         errors,
                         loc_inst,
                         "{} is defined by {} which is not in the layout",
                         v,
                         ebb
                     );
                 }
                 // The defining EBB dominates the instruction using this value.
-                if is_reachable && !self.expected_domtree.dominates(
-                    ebb,
-                    loc_inst,
-                    &self.func.layout,
-                ) {
+                if is_reachable
+                    && !self
+                        .expected_domtree
+                        .dominates(ebb, loc_inst, &self.func.layout)
+                {
                     return fatal!(
                         errors,
                         loc_inst,
                         "uses value arg from non-dominating {}",
                         ebb
                     );
                 }
             }
@@ -1554,17 +1554,18 @@ impl<'a> Verifier<'a> {
                 );
             }
 
             let mut encodings = isa
                 .legal_encodings(
                     &self.func,
                     &self.func.dfg[inst],
                     self.func.dfg.ctrl_typevar(inst),
-                ).peekable();
+                )
+                .peekable();
 
             if encodings.peek().is_none() {
                 return nonfatal!(
                     errors,
                     inst,
                     "Instruction failed to re-encode {}",
                     isa.encoding_info().display(encoding)
                 );
--- a/third_party/rust/cranelift-entity/.cargo-checksum.json
+++ b/third_party/rust/cranelift-entity/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"18b45cd5e51eaa9c5591e303a4fcdb759e740be967d02aaa154aaafc25281daa","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"96ceffbfd88fb06e3b41aa4d3087cffbbf8441d04eba7ab09662a72ab600a321","src/iter.rs":"cd2336dc29891e4e5060fb24f1d2f885a4606ea6580e9bd0f261843f12e31a1b","src/keys.rs":"5eca885b3e7ba19936c24066253bf47ebbd9d0a808a84dc910041e8e7be86349","src/lib.rs":"ccbf78e58d56ef1cb811e5ba451d6e8aa69a84c95f5cfe89c0d1f7ecdd26d15b","src/list.rs":"15d9762396439eb2e835401a7b85e190e097fa5eb863decfb67fb677f1093335","src/map.rs":"bcf7a78077056e4194581cbeb4b9d6bdd263ae1c6b261a0fc011ccda5f39bb4f","src/packed_option.rs":"19b2d49e0233e05e3dc39424187644af546934fe92b7200543fe10728ec831f6","src/primary.rs":"457f72222869abfc06109473d8a7435cf8acaef3888b4bd559608e135aa96395","src/set.rs":"fd687531bb01db9a22ed09486e378fdfe5842c84b603620f83fe8a42109df3cb","src/sparse.rs":"d2b7bd25c7e2b5ef2194feb08c635447953a4548d42ae26d080bb4ca44dfcc20"},"package":"fc9a0329208e5e0d7d4d6e64cd50985d4c4cbfdbeeb594ae2157a094b98e8dcc"}
\ No newline at end of file
+{"files":{"Cargo.toml":"a76fe4900987624504c61e99cf0c20c8dad64a42e77f0e43c0150c416679dd70","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"96ceffbfd88fb06e3b41aa4d3087cffbbf8441d04eba7ab09662a72ab600a321","src/boxed_slice.rs":"b17fae8f1bd8d970221ea0ff185a62dce0da2324abe95c8c8577b06c56cd11cb","src/iter.rs":"cd2336dc29891e4e5060fb24f1d2f885a4606ea6580e9bd0f261843f12e31a1b","src/keys.rs":"5eca885b3e7ba19936c24066253bf47ebbd9d0a808a84dc910041e8e7be86349","src/lib.rs":"dc3255cc56a089c696de14077ab7f49ceb95011c96ded22c6ca06d8418ff3b78","src/list.rs":"15d9762396439eb2e835401a7b85e190e097fa5eb863decfb67fb677f1093335","src/map.rs":"059ef89fa5022b56521162a2fe4b837d2bee84fdfcefd2c9973a910a6fa3417d","src/packed_option.rs":"19b2d49e0233e05e3dc39424187644af546934fe92b7200543fe10728ec831f6","src/primary.rs":"ccbafce1bc2fe15e35f844d32b511b3d422885dd9b4eaa20818b6f865cdb45f8","src/set.rs":"e227411e689c43c2cfea622ff46288598f05dfbe0e8ba652cfb04c6e3ecc192f","src/sparse.rs":"1a0a30f10203df406bd628aedb5242f71f51d35873142062c5b060ce3feae1ea"},"package":"d4df40e26c0cf7b4d86919cb995bb412ee3001cc18e4f3c83a903f30b7007d8b"}
\ No newline at end of file
--- a/third_party/rust/cranelift-entity/Cargo.toml
+++ b/third_party/rust/cranelift-entity/Cargo.toml
@@ -7,17 +7,17 @@
 #
 # 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]
 name = "cranelift-entity"
-version = "0.25.0"
+version = "0.26.0"
 authors = ["The Cranelift Project Developers"]
 description = "Data structures using entity references as mapping keys"
 documentation = "https://cranelift.readthedocs.io/"
 readme = "README.md"
 keywords = ["entity", "set", "map"]
 categories = ["no-std"]
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/CraneStation/cranelift"
new file mode 100644
--- /dev/null
+++ b/third_party/rust/cranelift-entity/src/boxed_slice.rs
@@ -0,0 +1,315 @@
+//! Boxed slices for `PrimaryMap`.
+
+use iter::{Iter, IterMut};
+use keys::Keys;
+use std::marker::PhantomData;
+use std::ops::{Index, IndexMut};
+use std::slice;
+use EntityRef;
+
+/// A slice mapping `K -> V` allocating dense entity references.
+///
+/// The `BoxedSlice` data structure uses the dense index space to implement a map with a boxed
+/// slice.
+#[derive(Debug, Clone)]
+pub struct BoxedSlice<K, V>
+where
+    K: EntityRef,
+{
+    elems: Box<[V]>,
+    unused: PhantomData<K>,
+}
+
+impl<K, V> BoxedSlice<K, V>
+where
+    K: EntityRef,
+{
+    /// Create a new slice from a raw pointer. A safer way to create slices is
+    /// to use `PrimaryMap::into_boxed_slice()`.
+    pub unsafe fn from_raw(raw: *mut [V]) -> Self {
+        Self {
+            elems: Box::from_raw(raw),
+            unused: PhantomData,
+        }
+    }
+
+    /// Check if `k` is a valid key in the map.
+    pub fn is_valid(&self, k: K) -> bool {
+        k.index() < self.elems.len()
+    }
+
+    /// Get the element at `k` if it exists.
+    pub fn get(&self, k: K) -> Option<&V> {
+        self.elems.get(k.index())
+    }
+
+    /// Get the element at `k` if it exists, mutable version.
+    pub fn get_mut(&mut self, k: K) -> Option<&mut V> {
+        self.elems.get_mut(k.index())
+    }
+
+    /// Is this map completely empty?
+    pub fn is_empty(&self) -> bool {
+        self.elems.is_empty()
+    }
+
+    /// Get the total number of entity references created.
+    pub fn len(&self) -> usize {
+        self.elems.len()
+    }
+
+    /// Iterate over all the keys in this map.
+    pub fn keys(&self) -> Keys<K> {
+        Keys::with_len(self.elems.len())
+    }
+
+    /// Iterate over all the values in this map.
+    pub fn values(&self) -> slice::Iter<V> {
+        self.elems.iter()
+    }
+
+    /// Iterate over all the values in this map, mutable edition.
+    pub fn values_mut(&mut self) -> slice::IterMut<V> {
+        self.elems.iter_mut()
+    }
+
+    /// Iterate over all the keys and values in this map.
+    pub fn iter(&self) -> Iter<K, V> {
+        Iter::new(self.elems.iter())
+    }
+
+    /// Iterate over all the keys and values in this map, mutable edition.
+    pub fn iter_mut(&mut self) -> IterMut<K, V> {
+        IterMut::new(self.elems.iter_mut())
+    }
+
+    /// Get the key that will be assigned to the next pushed value.
+    pub fn next_key(&self) -> K {
+        K::new(self.elems.len())
+    }
+
+    /// Returns the last element that was inserted in the map.
+    pub fn last(&self) -> Option<&V> {
+        self.elems.last()
+    }
+}
+
+/// Immutable indexing into a `BoxedSlice`.
+/// The indexed value must be in the map.
+impl<K, V> Index<K> for BoxedSlice<K, V>
+where
+    K: EntityRef,
+{
+    type Output = V;
+
+    fn index(&self, k: K) -> &V {
+        &self.elems[k.index()]
+    }
+}
+
+/// Mutable indexing into a `BoxedSlice`.
+impl<K, V> IndexMut<K> for BoxedSlice<K, V>
+where
+    K: EntityRef,
+{
+    fn index_mut(&mut self, k: K) -> &mut V {
+        &mut self.elems[k.index()]
+    }
+}
+
+impl<'a, K, V> IntoIterator for &'a BoxedSlice<K, V>
+where
+    K: EntityRef,
+{
+    type Item = (K, &'a V);
+    type IntoIter = Iter<'a, K, V>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        Iter::new(self.elems.iter())
+    }
+}
+
+impl<'a, K, V> IntoIterator for &'a mut BoxedSlice<K, V>
+where
+    K: EntityRef,
+{
+    type Item = (K, &'a mut V);
+    type IntoIter = IterMut<'a, K, V>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        IterMut::new(self.elems.iter_mut())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use primary::PrimaryMap;
+
+    // `EntityRef` impl for testing.
+    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+    struct E(u32);
+
+    impl EntityRef for E {
+        fn new(i: usize) -> Self {
+            E(i as u32)
+        }
+        fn index(self) -> usize {
+            self.0 as usize
+        }
+    }
+
+    #[test]
+    fn basic() {
+        let r0 = E(0);
+        let r1 = E(1);
+        let p = PrimaryMap::<E, isize>::new();
+        let m = p.into_boxed_slice();
+
+        let v: Vec<E> = m.keys().collect();
+        assert_eq!(v, []);
+
+        assert!(!m.is_valid(r0));
+        assert!(!m.is_valid(r1));
+    }
+
+    #[test]
+    fn iter() {
+        let mut p: PrimaryMap<E, usize> = PrimaryMap::new();
+        p.push(12);
+        p.push(33);
+        let mut m = p.into_boxed_slice();
+
+        let mut i = 0;
+        for (key, value) in &m {
+            assert_eq!(key.index(), i);
+            match i {
+                0 => assert_eq!(*value, 12),
+                1 => assert_eq!(*value, 33),
+                _ => panic!(),
+            }
+            i += 1;
+        }
+        i = 0;
+        for (key_mut, value_mut) in m.iter_mut() {
+            assert_eq!(key_mut.index(), i);
+            match i {
+                0 => assert_eq!(*value_mut, 12),
+                1 => assert_eq!(*value_mut, 33),
+                _ => panic!(),
+            }
+            i += 1;
+        }
+    }
+
+    #[test]
+    fn iter_rev() {
+        let mut p: PrimaryMap<E, usize> = PrimaryMap::new();
+        p.push(12);
+        p.push(33);
+        let mut m = p.into_boxed_slice();
+
+        let mut i = 2;
+        for (key, value) in m.iter().rev() {
+            i -= 1;
+            assert_eq!(key.index(), i);
+            match i {
+                0 => assert_eq!(*value, 12),
+                1 => assert_eq!(*value, 33),
+                _ => panic!(),
+            }
+        }
+
+        i = 2;