Merge mozilla-central to autoland
authorDaniel Varga <dvarga@mozilla.com>
Tue, 14 Aug 2018 01:04:58 +0300
changeset 486401 f4c0098a60888e14bbb168ea6c33ae30733ed3ea
parent 486400 d754118be3dbe31b2fb34ac16d03cdd605a7dbd7 (current diff)
parent 486387 8b39d1161075364a95bc2d1577b389411fe5c342 (diff)
child 486402 fcf8f41c1ae260f6695cf12e0528997361d55f28
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone63.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
js/src/jit/arm/BaselineIC-arm.cpp
js/src/jit/arm64/BaselineIC-arm64.cpp
js/src/jit/mips32/BaselineIC-mips32.cpp
js/src/jit/mips64/BaselineIC-mips64.cpp
js/src/jit/x64/BaselineIC-x64.cpp
js/src/jit/x86-shared/BaselineIC-x86-shared.cpp
js/src/jit/x86/BaselineIC-x86.cpp
--- 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.0.750
+Current extension version is: 2.0.760
 
-Taken from upstream commit: c8ee6331
+Taken from upstream commit: 1268aea2
--- 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.0.750';
-var pdfjsBuild = 'c8ee6331';
+var pdfjsVersion = '2.0.760';
+var pdfjsBuild = '1268aea2';
 var pdfjsSharedUtil = __w_pdfjs_require__(1);
 var pdfjsDisplayAPI = __w_pdfjs_require__(7);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(19);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(20);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(8);
 var pdfjsDisplaySVG = __w_pdfjs_require__(21);
 let pdfjsDisplayWorkerOptions = __w_pdfjs_require__(13);
 let pdfjsDisplayAPICompatibility = __w_pdfjs_require__(10);
@@ -4218,17 +4218,17 @@ function _fetchDocument(worker, source, 
     return Promise.reject(new Error('Worker was destroyed'));
   }
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
     docId,
-    apiVersion: '2.0.750',
+    apiVersion: '2.0.760',
     source: {
       data: source.data,
       url: source.url,
       password: source.password,
       disableAutoFetch: source.disableAutoFetch,
       rangeChunkSize: source.rangeChunkSize,
       length: source.length
     },
@@ -5309,16 +5309,19 @@ var WorkerTransport = function WorkerTra
         pageIndex,
         intent
       });
     },
     getDestinations: function WorkerTransport_getDestinations() {
       return this.messageHandler.sendWithPromise('GetDestinations', null);
     },
     getDestination: function WorkerTransport_getDestination(id) {
+      if (typeof id !== 'string') {
+        return Promise.reject(new Error('Invalid destination request.'));
+      }
       return this.messageHandler.sendWithPromise('GetDestination', { id });
     },
     getPageLabels: function WorkerTransport_getPageLabels() {
       return this.messageHandler.sendWithPromise('GetPageLabels', null);
     },
     getPageMode() {
       return this.messageHandler.sendWithPromise('GetPageMode', null);
     },
@@ -5558,18 +5561,18 @@ var InternalRenderTask = function Intern
         }
       });
     }
   };
   return InternalRenderTask;
 }();
 var version, build;
 {
-  exports.version = version = '2.0.750';
-  exports.build = build = 'c8ee6331';
+  exports.version = version = '2.0.760';
+  exports.build = build = '1268aea2';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
--- 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.0.750';
-var pdfjsBuild = 'c8ee6331';
+var pdfjsVersion = '2.0.760';
+var pdfjsBuild = '1268aea2';
 var pdfjsCoreWorker = __w_pdfjs_require__(1);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -322,17 +322,17 @@ var WorkerMessageHandler = {
     });
   },
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.0.750';
+    let workerVersion = '2.0.760';
     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';
     var handler = new _message_handler.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
@@ -576,19 +576,19 @@ var WorkerMessageHandler = {
       pdfManager.requestLoadedStream();
       return pdfManager.onLoadedStream().then(function (stream) {
         return stream.bytes;
       });
     });
     handler.on('GetStats', function wphSetupGetStats(data) {
       return pdfManager.pdfDocument.xref.stats;
     });
-    handler.on('GetAnnotations', function wphSetupGetAnnotations(data) {
-      return pdfManager.getPage(data.pageIndex).then(function (page) {
-        return pdfManager.ensure(page, 'getAnnotationsData', [data.intent]);
+    handler.on('GetAnnotations', function ({ pageIndex, intent }) {
+      return pdfManager.getPage(pageIndex).then(function (page) {
+        return page.getAnnotationsData(intent);
       });
     });
     handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
       var pageIndex = data.pageIndex;
       pdfManager.getPage(pageIndex).then(function (page) {
         var task = new WorkerTask('RenderPageRequest: page ' + pageIndex);
         startWorkerTask(task);
         var pageNum = pageIndex + 1;
@@ -5388,18 +5388,17 @@ var Page = function PageClosure() {
           stream: contentStream,
           task,
           resources: this.resources,
           operatorList: opList
         }).then(function () {
           return opList;
         });
       });
-      var annotationsPromise = this.pdfManager.ensure(this, 'annotations');
-      return Promise.all([pageListPromise, annotationsPromise]).then(function ([pageOpList, annotations]) {
+      return Promise.all([pageListPromise, this._parsedAnnotations]).then(function ([pageOpList, annotations]) {
         if (annotations.length === 0) {
           pageOpList.flush(true);
           return pageOpList;
         }
         var i,
             ii,
             opListPromises = [];
         for (i = 0, ii = annotations.length; i < ii; i++) {
@@ -5439,37 +5438,47 @@ var Page = function PageClosure() {
           task,
           resources: this.resources,
           normalizeWhitespace,
           combineTextItems,
           sink
         });
       });
     },
-    getAnnotationsData: function Page_getAnnotationsData(intent) {
-      var annotations = this.annotations;
-      var annotationsData = [];
-      for (var i = 0, n = annotations.length; i < n; ++i) {
-        if (!intent || isAnnotationRenderable(annotations[i], intent)) {
-          annotationsData.push(annotations[i].data);
-        }
-      }
-      return annotationsData;
+    getAnnotationsData(intent) {
+      return this._parsedAnnotations.then(function (annotations) {
+        let annotationsData = [];
+        for (let i = 0, ii = annotations.length; i < ii; i++) {
+          if (!intent || isAnnotationRenderable(annotations[i], intent)) {
+            annotationsData.push(annotations[i].data);
+          }
+        }
+        return annotationsData;
+      });
     },
     get annotations() {
-      var annotations = [];
-      var annotationRefs = this._getInheritableProperty('Annots') || [];
-      for (var i = 0, n = annotationRefs.length; i < n; ++i) {
-        var annotationRef = annotationRefs[i];
-        var annotation = _annotation.AnnotationFactory.create(this.xref, annotationRef, this.pdfManager, this.idFactory);
-        if (annotation) {
-          annotations.push(annotation);
-        }
-      }
-      return (0, _util.shadow)(this, 'annotations', annotations);
+      return (0, _util.shadow)(this, 'annotations', this._getInheritableProperty('Annots') || []);
+    },
+    get _parsedAnnotations() {
+      const parsedAnnotations = this.pdfManager.ensure(this, 'annotations').then(() => {
+        const annotationRefs = this.annotations;
+        const annotationPromises = [];
+        for (let i = 0, ii = annotationRefs.length; i < ii; i++) {
+          annotationPromises.push(_annotation.AnnotationFactory.create(this.xref, annotationRefs[i], this.pdfManager, this.idFactory));
+        }
+        return Promise.all(annotationPromises).then(function (annotations) {
+          return annotations.filter(function isDefined(annotation) {
+            return !!annotation;
+          });
+        }, function (reason) {
+          (0, _util.warn)(`_parsedAnnotations: "${reason}".`);
+          return [];
+        });
+      });
+      return (0, _util.shadow)(this, '_parsedAnnotations', parsedAnnotations);
     }
   };
   return Page;
 }();
 var PDFDocument = function PDFDocumentClosure() {
   var FINGERPRINT_FIRST_BYTES = 1024;
   var EMPTY_FINGERPRINT = '\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00\x00';
   function PDFDocument(pdfManager, arg) {
@@ -5754,16 +5763,19 @@ var _primitives = __w_pdfjs_require__(12
 var _parser = __w_pdfjs_require__(13);
 
 var _chunked_stream = __w_pdfjs_require__(9);
 
 var _crypto = __w_pdfjs_require__(24);
 
 var _colorspace = __w_pdfjs_require__(25);
 
+function fetchDestination(dest) {
+  return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
+}
 var Catalog = function CatalogClosure() {
   function Catalog(pdfManager, xref) {
     this.pdfManager = pdfManager;
     this.xref = xref;
     this.catDict = xref.getCatalogObj();
     if (!(0, _primitives.isDict)(this.catDict)) {
       throw new _util.FormatError('catalog object is not a dictionary');
     }
@@ -5893,72 +5905,46 @@ var Catalog = function CatalogClosure() 
     get numPages() {
       var obj = this.toplevelPagesDict.get('Count');
       if (!Number.isInteger(obj)) {
         throw new _util.FormatError('page count in top level pages object is not an integer');
       }
       return (0, _util.shadow)(this, 'numPages', obj);
     },
     get destinations() {
-      function fetchDestination(dest) {
-        return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
-      }
-      var xref = this.xref;
-      var dests = {},
-          nameTreeRef,
-          nameDictionaryRef;
-      var obj = this.catDict.get('Names');
-      if (obj && obj.has('Dests')) {
-        nameTreeRef = obj.getRaw('Dests');
-      } else if (this.catDict.has('Dests')) {
-        nameDictionaryRef = this.catDict.get('Dests');
-      }
-      if (nameDictionaryRef) {
-        obj = nameDictionaryRef;
-        obj.forEach(function catalogForEach(key, value) {
-          if (!value) {
-            return;
-          }
-          dests[key] = fetchDestination(value);
-        });
-      }
-      if (nameTreeRef) {
-        var nameTree = new NameTree(nameTreeRef, xref);
-        var names = nameTree.getAll();
-        for (var name in names) {
+      const obj = this._readDests(),
+            dests = Object.create(null);
+      if (obj instanceof NameTree) {
+        const names = obj.getAll();
+        for (let name in names) {
           dests[name] = fetchDestination(names[name]);
         }
+      } else if (obj instanceof _primitives.Dict) {
+        obj.forEach(function (key, value) {
+          if (value) {
+            dests[key] = fetchDestination(value);
+          }
+        });
       }
       return (0, _util.shadow)(this, 'destinations', dests);
     },
-    getDestination: function Catalog_getDestination(destinationId) {
-      function fetchDestination(dest) {
-        return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
-      }
-      var xref = this.xref;
-      var dest = null,
-          nameTreeRef,
-          nameDictionaryRef;
-      var obj = this.catDict.get('Names');
+    getDestination(destinationId) {
+      const obj = this._readDests();
+      if (obj instanceof NameTree || obj instanceof _primitives.Dict) {
+        return fetchDestination(obj.get(destinationId) || null);
+      }
+      return null;
+    },
+    _readDests() {
+      const obj = this.catDict.get('Names');
       if (obj && obj.has('Dests')) {
-        nameTreeRef = obj.getRaw('Dests');
+        return new NameTree(obj.getRaw('Dests'), this.xref);
       } else if (this.catDict.has('Dests')) {
-        nameDictionaryRef = this.catDict.get('Dests');
-      }
-      if (nameDictionaryRef) {
-        var value = nameDictionaryRef.get(destinationId);
-        if (value) {
-          dest = fetchDestination(value);
-        }
-      }
-      if (nameTreeRef) {
-        var nameTree = new NameTree(nameTreeRef, xref);
-        dest = fetchDestination(nameTree.get(destinationId));
-      }
-      return dest;
+        return this.catDict.get('Dests');
+      }
     },
     get pageLabels() {
       var obj = null;
       try {
         obj = this.readPageLabels();
       } catch (ex) {
         if (ex instanceof _util.MissingDataException) {
           throw ex;
@@ -17705,16 +17691,19 @@ var _primitives = __w_pdfjs_require__(12
 var _colorspace = __w_pdfjs_require__(25);
 
 var _operator_list = __w_pdfjs_require__(27);
 
 var _stream = __w_pdfjs_require__(14);
 
 class AnnotationFactory {
   static create(xref, ref, pdfManager, idFactory) {
+    return pdfManager.ensure(this, '_create', [xref, ref, pdfManager, idFactory]);
+  }
+  static _create(xref, ref, pdfManager, idFactory) {
     let dict = xref.fetchIfRef(ref);
     if (!(0, _primitives.isDict)(dict)) {
       return;
     }
     let id = (0, _primitives.isRef)(ref) ? ref.toString() : 'annot_' + idFactory.createObjId();
     let subtype = dict.get('Subtype');
     subtype = (0, _primitives.isName)(subtype) ? subtype.name : null;
     let parameters = {
@@ -22807,27 +22796,25 @@ var Font = function FontClosure() {
       if (composite) {
         fileType = 'CIDFontType2';
       } else {
         fileType = 'OpenType';
       }
     } else if (isType1File(file)) {
       if (composite) {
         fileType = 'CIDFontType0';
-      } else if (type === 'MMType1') {
-        fileType = 'MMType1';
-      } else {
-        fileType = 'Type1';
+      } else {
+        fileType = type === 'MMType1' ? 'MMType1' : 'Type1';
       }
     } else if (isCFFFile(file)) {
       if (composite) {
         fileType = 'CIDFontType0';
         fileSubtype = 'CIDFontType0C';
       } else {
-        fileType = 'Type1';
+        fileType = type === 'MMType1' ? 'MMType1' : 'Type1';
         fileSubtype = 'Type1C';
       }
     } else {
       (0, _util.warn)('getFontFileType: Unable to detect correct font file Type/Subtype.');
       fileType = type;
       fileSubtype = subtype;
     }
     return [fileType, fileSubtype];
--- 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.0.719
+  release: version 2.0.760
 
   # 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/build/moz.configure/checks.configure
+++ b/build/moz.configure/checks.configure
@@ -85,26 +85,30 @@ def checking(what, callback=None):
 #   The default is to create an option for the environment variable `var`.
 #   This argument allows to use a different kind of option (possibly using a
 #   configure flag), or doing some pre-processing with a @depends function.
 # - `allow_missing` indicates whether not finding the program is an error.
 # - `paths` is a list of paths or @depends function returning a list of paths
 #   that will cause the given path(s) to be searched rather than $PATH. Input
 #   paths may either be individual paths or delimited by os.pathsep, to allow
 #   passing $PATH (for example) as an element.
+# - `paths_have_priority` means that any programs found early in the PATH
+#   will be prioritized over programs found later in the PATH.  The default is
+#   False, meaning that any of the programs earlier in the program list will be
+#   given priority, no matter where in the PATH they are found.
 #
 # The simplest form is:
 #   check_prog('PROG', ('a', 'b'))
 # This will look for 'a' or 'b' in $PATH, and set_config PROG to the one
 # it can find. If PROG is already set from the environment or command line,
 # use that value instead.
 @template
 @imports(_from='mozbuild.shellutil', _import='quote')
 def check_prog(var, progs, what=None, input=None, allow_missing=False,
-               paths=None, when=None):
+               paths=None, paths_have_priority=False, when=None):
     if input is not None:
         # Wrap input with type checking and normalization.
         @depends(input, when=when)
         def input(value):
             if not value:
                 return
             if isinstance(value, str):
                 return (value,)
@@ -126,21 +130,29 @@ def check_prog(var, progs, what=None, in
     @checking('for %s' % what, lambda x: quote(x) if x else 'not found')
     def check(value, progs, paths):
         if progs is None:
             progs = ()
 
         if not isinstance(progs, (tuple, list)):
             configure_error('progs must resolve to a list or tuple!')
 
-        for prog in value or progs:
-            log.debug('%s: Trying %s', var.lower(), quote(prog))
-            result = find_program(prog, paths)
-            if result:
-                return result
+        if paths_have_priority:
+            for path in paths:
+                for prog in value or progs:
+                    log.debug('%s: Trying %s', var.lower(), quote(prog))
+                    result = find_program(prog, [path])
+                    if result:
+                        return result
+        else:
+            for prog in value or progs:
+                log.debug('%s: Trying %s', var.lower(), quote(prog))
+                result = find_program(prog, paths)
+                if result:
+                    return result
 
         if not allow_missing or value:
             raise FatalCheckError('Cannot find %s' % what)
 
     @depends_if(check, progs, when=when)
     def normalized_for_config(value, progs):
         return ':' if value is None else value
 
--- a/build/moz.configure/node.configure
+++ b/build/moz.configure/node.configure
@@ -30,17 +30,18 @@ def node_toolchain_search_path(host):
     updated_path = [mozbuild_node_path] + path
 
     return updated_path
 
 # "nodejs" is first in the tuple on the assumption that it's only likely to
 # exist on systems (probably linux distros) where there is a program in the path
 # called "node" that does something else.
 nodejs = check_prog('NODEJS', ('nodejs', 'node',),
-                    allow_missing=True, paths=node_toolchain_search_path)
+                    allow_missing=True, paths=node_toolchain_search_path,
+                    paths_have_priority=True)
 
 
 @depends_if(nodejs)
 @checking('for node.js version')
 @imports('os')
 @imports('re')
 @imports('subprocess')
 def nodejs_version(node):
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -937,33 +937,29 @@ CopyingStructuredCloneReadCallback(JSCon
       result.set(&wrappedFile.toObject());
 
       return result;
     }
 
     if (aTag == SCTAG_DOM_MUTABLEFILE) {
       MOZ_ASSERT(file.mType == StructuredCloneFile::eMutableFile);
 
-      RefPtr<IDBMutableFile> mutableFile = file.mMutableFile;
-
       JS::Rooted<JS::Value> wrappedMutableFile(aCx);
-      if (NS_WARN_IF(!ToJSValue(aCx, mutableFile, &wrappedMutableFile))) {
+      if (NS_WARN_IF(!ToJSValue(aCx, file.mMutableFile, &wrappedMutableFile))) {
         return nullptr;
       }
 
       result.set(&wrappedMutableFile.toObject());
 
       return result;
     }
 
     MOZ_ASSERT(file.mType == StructuredCloneFile::eWasmBytecode);
 
-    RefPtr<JS::WasmModule> module = file.mWasmModule;
-
-    JS::Rooted<JSObject*> wrappedModule(aCx, module->createObject(aCx));
+    JS::Rooted<JSObject*> wrappedModule(aCx, file.mWasmModule->createObject(aCx));
     if (NS_WARN_IF(!wrappedModule)) {
       return nullptr;
     }
 
     result.set(wrappedModule);
 
     return result;
   }
--- a/dom/script/ScriptSettings.cpp
+++ b/dom/script/ScriptSettings.cpp
@@ -666,17 +666,18 @@ AutoEntryScript::AutoEntryScript(nsIGlob
   }
 }
 
 AutoEntryScript::AutoEntryScript(JSObject* aObject,
                                  const char* aReason,
                                  bool aIsMainThread)
   : AutoEntryScript(xpc::NativeGlobal(aObject), aReason, aIsMainThread)
 {
-  MOZ_ASSERT(!js::IsCrossCompartmentWrapper(aObject));
+  // xpc::NativeGlobal uses JS::GetNonCCWObjectGlobal, which asserts that
+  // aObject is not a CCW.
 }
 
 AutoEntryScript::~AutoEntryScript()
 {
 }
 
 AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx,
                                                             const char* aReason)
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -2697,34 +2697,35 @@ ASTSerializer::expression(ParseNode* pn,
                builder.unaryExpression(op, expr, &pn->pn_pos, dst);
       }
 
       case ParseNodeKind::New:
       case ParseNodeKind::TaggedTemplate:
       case ParseNodeKind::Call:
       case ParseNodeKind::SuperCall:
       {
-        ParseNode* next = pn->pn_head;
-        MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
+        ParseNode* pn_callee = pn->pn_left;
+        ParseNode* pn_args = pn->pn_right;
+        MOZ_ASSERT(pn->pn_pos.encloses(pn_callee->pn_pos));
 
         RootedValue callee(cx);
         if (pn->isKind(ParseNodeKind::SuperCall)) {
-            MOZ_ASSERT(next->isKind(ParseNodeKind::SuperBase));
-            if (!builder.super(&next->pn_pos, &callee))
+            MOZ_ASSERT(pn_callee->isKind(ParseNodeKind::SuperBase));
+            if (!builder.super(&pn_callee->pn_pos, &callee))
                 return false;
         } else {
-            if (!expression(next, &callee))
+            if (!expression(pn_callee, &callee))
                 return false;
         }
 
         NodeVector args(cx);
-        if (!args.reserve(pn->pn_count - 1))
+        if (!args.reserve(pn_args->pn_count))
             return false;
 
-        for (next = next->pn_next; next; next = next->pn_next) {
+        for (ParseNode* next = pn_args->pn_head; next; next = next->pn_next) {
             MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
 
             RootedValue arg(cx);
             if (!expression(next, &arg))
                 return false;
             args.infallibleAppend(arg);
         }
 
@@ -2735,27 +2736,27 @@ ASTSerializer::expression(ParseNode* pn,
         return pn->isKind(ParseNodeKind::New)
                ? builder.newExpression(callee, args, &pn->pn_pos, dst)
 
             : builder.callExpression(callee, args, &pn->pn_pos, dst);
       }
 
       case ParseNodeKind::Dot:
       {
-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos));
+        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
 
         RootedValue expr(cx);
         RootedValue propname(cx);
-        RootedAtom pnAtom(cx, pn->pn_atom);
+        RootedAtom pnAtom(cx, pn->pn_right->pn_atom);
 
         if (pn->as<PropertyAccess>().isSuper()) {
-            if (!builder.super(&pn->pn_expr->pn_pos, &expr))
+            if (!builder.super(&pn->pn_left->pn_pos, &expr))
                 return false;
         } else {
-            if (!expression(pn->pn_expr, &expr))
+            if (!expression(pn->pn_left, &expr))
                 return false;
         }
 
         return identifier(pnAtom, nullptr, &propname) &&
                builder.memberExpression(false, expr, propname, &pn->pn_pos, dst);
       }
 
       case ParseNodeKind::Elem:
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -74,33 +74,33 @@ js::CreateRegExpMatchResult(JSContext* c
             if (!str)
                 return false;
             arr->setDenseInitializedLength(i + 1);
             arr->initDenseElement(i, StringValue(str));
         }
     }
 
     /* Step 20 (reordered).
-     * Set the |index| property. (TemplateObject positions it in slot 0) */
-    arr->setSlot(0, Int32Value(matches[0].start));
+     * Set the |index| property. */
+    arr->setSlot(RegExpRealm::MatchResultObjectIndexSlot, Int32Value(matches[0].start));
 
     /* Step 21 (reordered).
-     * Set the |input| property. (TemplateObject positions it in slot 1) */
-    arr->setSlot(1, StringValue(input));
+     * Set the |input| property. */
+    arr->setSlot(RegExpRealm::MatchResultObjectInputSlot, StringValue(input));
 
 #ifdef DEBUG
     RootedValue test(cx);
     RootedId id(cx, NameToId(cx->names().index));
     if (!NativeGetProperty(cx, arr, id, &test))
         return false;
-    MOZ_ASSERT(test == arr->getSlot(0));
+    MOZ_ASSERT(test == arr->getSlot(RegExpRealm::MatchResultObjectIndexSlot));
     id = NameToId(cx->names().input);
     if (!NativeGetProperty(cx, arr, id, &test))
         return false;
-    MOZ_ASSERT(test == arr->getSlot(1));
+    MOZ_ASSERT(test == arr->getSlot(RegExpRealm::MatchResultObjectInputSlot));
 #endif
 
     /* Step 25. */
     rval.setObject(*arr);
     return true;
 }
 
 static int32_t
@@ -1020,26 +1020,28 @@ js::RegExpMatcher(JSContext* cx, unsigne
 }
 
 /*
  * Separate interface for use by IonMonkey.
  * This code cannot re-enter Ion code.
  */
 bool
 js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                     int32_t lastIndex,
+                     int32_t maybeLastIndex,
                      MatchPairs* maybeMatches, MutableHandleValue output)
 {
-    MOZ_ASSERT(lastIndex >= 0);
-
     // The MatchPairs will always be passed in, but RegExp execution was
     // successful only if the pairs have actually been filled in.
-    if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
+    if (maybeMatches && maybeMatches->pairsRaw()[0] > MatchPair::NoMatch)
         return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
-    return RegExpMatcherImpl(cx, regexp, input, lastIndex, output);
+
+    // |maybeLastIndex| only contains a valid value when the RegExp execution
+    // was not successful.
+    MOZ_ASSERT(maybeLastIndex >= 0);
+    return RegExpMatcherImpl(cx, regexp, input, maybeLastIndex, output);
 }
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
  * This code is inlined in CodeGenerator.cpp generateRegExpSearcherStub,
  * changes to this code need to get reflected in there too.
  */
@@ -1101,17 +1103,17 @@ js::RegExpSearcher(JSContext* cx, unsign
 bool
 js::RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
                       int32_t lastIndex, MatchPairs* maybeMatches, int32_t* result)
 {
     MOZ_ASSERT(lastIndex >= 0);
 
     // The MatchPairs will always be passed in, but RegExp execution was
     // successful only if the pairs have actually been filled in.
-    if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0) {
+    if (maybeMatches && maybeMatches->pairsRaw()[0] > MatchPair::NoMatch) {
         *result = CreateRegExpSearchResult(*maybeMatches);
         return true;
     }
     return RegExpSearcherImpl(cx, regexp, input, lastIndex, result);
 }
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -36,17 +36,17 @@ MOZ_MUST_USE bool
 CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
                         MutableHandleValue rval);
 
 extern MOZ_MUST_USE bool
 RegExpMatcher(JSContext* cx, unsigned argc, Value* vp);
 
 extern MOZ_MUST_USE bool
 RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                 int32_t lastIndex, MatchPairs* maybeMatches, MutableHandleValue output);
+                 int32_t maybeLastIndex, MatchPairs* maybeMatches, MutableHandleValue output);
 
 extern MOZ_MUST_USE bool
 RegExpSearcher(JSContext* cx, unsigned argc, Value* vp);
 
 extern MOZ_MUST_USE bool
 RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
                   int32_t lastIndex, MatchPairs* maybeMatches, int32_t* result);
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -997,19 +997,17 @@ StructMetaTypeDescr::createFromArrays(JS
     descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
 
     if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
         return nullptr;
 
     if (!CreateTraceList(cx, descr))
         return nullptr;
 
-    if (!cx->zone()->addTypeDescrObject(cx, descr) ||
-        !cx->zone()->addTypeDescrObject(cx, fieldTypeVec))
-    {
+    if (!cx->zone()->addTypeDescrObject(cx, descr)) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     return descr;
 }
 
 bool
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -389,16 +389,20 @@ function isOverridableField(initialCSU, 
     if (field == 'GetNativeContext')
         return false;
     if (field == "GetGlobalJSObject")
         return false;
     if (field == "GetIsMainThread")
         return false;
     if (field == "GetThreadFromPRThread")
         return false;
+    if (field == "ConstructUbiNode")
+        return false;
+    if (initialCSU == 'nsIXPCScriptable' && field == "GetScriptableFlags")
+        return false;
     if (initialCSU == 'nsIXPConnectJSObjectHolder' && field == 'GetJSObject')
         return false;
     if (initialCSU == 'nsIXPConnect' && field == 'GetSafeJSContext')
         return false;
 
     // nsIScriptSecurityManager is not [builtinclass], but smaug says "the
     // interface definitely should be builtinclass", which is good enough.
     if (initialCSU == 'nsIScriptSecurityManager' && field == 'IsSystemPrincipal')
--- a/js/src/devtools/rootAnalysis/run_complete
+++ b/js/src/devtools/rootAnalysis/run_complete
@@ -21,17 +21,17 @@
 # various run_monitor processes to be running in the background (maybe on other
 # machines) and watching a shared poll_file for jobs. if the output directory
 # for this script already exists then an incremental analysis will be performed
 # and the reports will only reflect the changes since the earlier run.
 
 use strict;
 use warnings;
 use IO::Handle;
-use File::Basename qw(dirname);
+use File::Basename qw(basename dirname);
 use Getopt::Long;
 use Cwd;
 
 #################################
 # environment specific settings #
 #################################
 
 my $WORKDIR;
@@ -244,23 +244,27 @@ sub run_build
         push(@extra, "-fplugin-arg-xgill-annfile=$ann_file")
             if ($ann_file ne "" && -e $ann_file);
         print CONFIG join(" ", @extra) . "\n";
         close(CONFIG);
 
 	# Tell the wrapper where to find the config
 	$ENV{"XGILL_CONFIG"} = Cwd::abs_path($config_file);
 
-        # update the PATH so that the build will see the wrappers.
+        # If overriding $CC, use GCCDIR to tell the wrapper scripts where the
+        # real compiler is. If $CC is not set, then the wrapper script will
+        # search $PATH anyway.
         if (exists $ENV{CC}) {
-            $ENV{PATH} = dirname($ENV{CC}) . ":$ENV{PATH}";
-            delete $ENV{CC};
-            delete $ENV{CXX};
+            $ENV{GCCDIR} = dirname($ENV{CC});
         }
-        $ENV{"PATH"} = "$wrap_dir:" . $ENV{"PATH"};
+
+        # Force the wrapper scripts to be run in place of the compiler during
+        # whatever build process we use.
+        $ENV{CC} = "$wrap_dir/" . basename($ENV{CC} // "gcc");
+        $ENV{CXX} = "$wrap_dir/" . basename($ENV{CXX} // "g++");
 
         # do the build, cleaning if necessary.
         chdir $build_dir;
         clean_project() if ($do_clean);
         my $exit_status = build_project();
 
         # signal the manager that it's over.
         system("$xsource -remote=$address -end-manager");
--- a/js/src/frontend/BinSource-auto.cpp
+++ b/js/src/frontend/BinSource-auto.cpp
@@ -3457,19 +3457,18 @@ BinASTParser<Tok>::parseInterfaceCallExp
          && !parseContext_->innermostScope()->lookupDeclaredNameForAdd(callee->name())) {
             // This is a direct call to `eval`.
             if (!parseContext_->sc()->hasDirectEval())
                 return raiseMissingDirectEvalInAssertedScope();
 
             op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
         }
     }
-    auto result = arguments;
-    result->setKind(ParseNodeKind::Call);
-    result->prepend(callee);
+
+    BINJS_TRY_DECL(result, factory_.newCall(callee, arguments));
     result->setOp(op);
     return result;
 }
 
 
 /*
  interface CatchClause : Node {
     AssertedParameterScope? bindingScope;
@@ -3737,17 +3736,17 @@ BinASTParser<Tok>::parseInterfaceCompute
     const BinField expected_fields[2] = { BinField::Object, BinField::Expression };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
 
     BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
 
     BINJS_MOZ_TRY_DECL(expression, parseExpression());
 
-    BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
+    BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
     return result;
 }
 
 
 /*
  interface ComputedMemberExpression : Node {
     (Expression or Super) object;
     Expression expression;
@@ -3781,17 +3780,17 @@ BinASTParser<Tok>::parseInterfaceCompute
     const BinField expected_fields[2] = { BinField::Object, BinField::Expression };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
 
     BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
 
     BINJS_MOZ_TRY_DECL(expression, parseExpression());
 
-    BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
+    BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
     return result;
 }
 
 
 /*
  interface ComputedPropertyName : Node {
     Expression expression;
  }
@@ -5672,20 +5671,17 @@ BinASTParser<Tok>::parseInterfaceNewExpr
     const BinField expected_fields[2] = { BinField::Callee, BinField::Arguments };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
 
     BINJS_MOZ_TRY_DECL(callee, parseExpression());
 
     BINJS_MOZ_TRY_DECL(arguments, parseArguments());
 
-    auto result = arguments;
-    result->setKind(ParseNodeKind::New);
-    result->prepend(callee);
-    result->setOp(JSOP_NEW);
+    BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments));
     return result;
 }
 
 
 /*
  interface NewTargetExpression : Node {
  }
 */
@@ -6192,23 +6188,28 @@ BinASTParser<Tok>::parseInterfaceStaticM
 {
     MOZ_ASSERT(kind == BinKind::StaticMemberAssignmentTarget);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
     const BinField expected_fields[2] = { BinField::Object, BinField::Property };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
+    size_t nameStart;
 
     BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
-
     RootedAtom property(cx_);
-    MOZ_TRY_VAR(property, tokenizer_->readAtom());
-
-    BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
+    {
+        nameStart = tokenizer_->offset();
+        MOZ_TRY_VAR(property, tokenizer_->readAtom());
+
+    }
+
+    BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
+    BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
     return result;
 }
 
 
 /*
  interface StaticMemberExpression : Node {
     (Expression or Super) object;
     IdentifierName property;
@@ -6237,23 +6238,28 @@ BinASTParser<Tok>::parseInterfaceStaticM
 {
     MOZ_ASSERT(kind == BinKind::StaticMemberExpression);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
     const BinField expected_fields[2] = { BinField::Object, BinField::Property };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
+    size_t nameStart;
 
     BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
-
     RootedAtom property(cx_);
-    MOZ_TRY_VAR(property, tokenizer_->readAtom());
-
-    BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
+    {
+        nameStart = tokenizer_->offset();
+        MOZ_TRY_VAR(property, tokenizer_->readAtom());
+
+    }
+
+    BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
+    BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
     return result;
 }
 
 
 /*
  interface Super : Node {
  }
 */
@@ -7376,17 +7382,17 @@ BinASTParser<Tok>::parseVariableDeclarat
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseArguments()
 {
     uint32_t length;
     AutoList guard(*tokenizer_);
 
     const auto start = tokenizer_->offset();
     MOZ_TRY(tokenizer_->enterList(length, guard));
-    BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+    BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::Arguments, tokenizer_->pos(start)));
 
     for (uint32_t i = 0; i < length; ++i) {
         BINJS_MOZ_TRY_DECL(item, parseSpreadElementOrExpression());
         factory_.addList(/* list = */ result, /* kid = */ item);
     }
 
     MOZ_TRY(guard.done());
     return result;
--- a/js/src/frontend/BinSource.yaml
+++ b/js/src/frontend/BinSource.yaml
@@ -202,17 +202,17 @@ hpp:
 
             } // namespace frontend
             } // namespace js
 
             #endif // frontend_BinToken_h
 
 Arguments:
     init:
-        BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+        BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::Arguments, tokenizer_->pos(start)));
     append:
         factory_.addList(/* list = */ result, /* kid = */ item);
 
 ArrayExpression:
     build:
         auto result = elements;
 
 AssertedBlockScope:
@@ -423,19 +423,18 @@ CallExpression:
              && !parseContext_->innermostScope()->lookupDeclaredNameForAdd(callee->name())) {
                 // This is a direct call to `eval`.
                 if (!parseContext_->sc()->hasDirectEval())
                     return raiseMissingDirectEvalInAssertedScope();
 
                 op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
             }
         }
-        auto result = arguments;
-        result->setKind(ParseNodeKind::Call);
-        result->prepend(callee);
+
+        BINJS_TRY_DECL(result, factory_.newCall(callee, arguments));
         result->setOp(op);
 
 
 CatchClause:
     init: |
         ParseContext::Statement stmt(parseContext_, StatementKind::Catch);
         ParseContext::Scope currentScope(cx_, parseContext_, usedNames_);
         BINJS_TRY(currentScope.init(parseContext_));
@@ -484,21 +483,21 @@ CompoundAssignmentExpression:
           case CompoundAssignmentOperator::BitAndAssign:
             pnk = ParseNodeKind::BitAndAssign;
             break;
         }
         BINJS_TRY_DECL(result, factory_.newAssignment(pnk, binding, expression));
 
 ComputedMemberAssignmentTarget:
     build: |
-        BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
+        BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
 
 ComputedMemberExpression:
     build: |
-        BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
+        BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));
 
 ConditionalExpression:
     build: |
         BINJS_TRY_DECL(result, factory_.newConditional(test, consequent, alternate));
 
 ContinueStatement:
     fields:
         label:
@@ -826,20 +825,17 @@ LiteralRegExpExpression:
         BINJS_TRY_DECL(result, factory_.newRegExp(reobj, tokenizer_->pos(start), *this));
 
 LiteralStringExpression:
     build:
         BINJS_TRY_DECL(result, factory_.newStringLiteral(value, tokenizer_->pos(start)));
 
 NewExpression:
     build: |
-        auto result = arguments;
-        result->setKind(ParseNodeKind::New);
-        result->prepend(callee);
-        result->setOp(JSOP_NEW);
+        BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments));
 
 ObjectExpression:
     build:
         auto result = properties;
 
 OptionalAssertedBlockScope:
     type-ok:
         Ok
@@ -917,22 +913,38 @@ SwitchStatementWithDefault:
             ParseNode* next = iter->pn_next;
             factory_.addList(cases, iter);
             iter = next;
         }
         BINJS_TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
         BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope, true));
 
 StaticMemberAssignmentTarget:
+    init:
+        size_t nameStart;
+    fields:
+        property:
+            block:
+                before: |
+                    nameStart = tokenizer_->offset();
     build: |
-        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
+        BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
+        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
 
 StaticMemberExpression:
+    init:
+        size_t nameStart;
+    fields:
+        property:
+            block:
+                before: |
+                    nameStart = tokenizer_->offset();
     build: |
-        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
+        BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
+        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));
 
 ThisExpression:
     build: |
         if (parseContext_->isFunctionBox())
             parseContext_->functionBox()->usesThis = true;
 
         TokenPos pos = tokenizer_->pos(start);
         ParseNode* thisName(nullptr);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1017,17 +1017,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       case ParseNodeKind::Continue:
       case ParseNodeKind::Debugger:
         MOZ_ASSERT(pn->isArity(PN_NULLARY));
         *answer = true;
         return true;
 
       // Watch out for getters!
       case ParseNodeKind::Dot:
-        MOZ_ASSERT(pn->isArity(PN_NAME));
+        MOZ_ASSERT(pn->isArity(PN_BINARY));
         *answer = true;
         return true;
 
       // Unary cases with side effects only if the child has them.
       case ParseNodeKind::TypeOfExpr:
       case ParseNodeKind::Void:
       case ParseNodeKind::Not:
         MOZ_ASSERT(pn->isArity(PN_UNARY));
@@ -1255,16 +1255,24 @@ BytecodeEmitter::checkSideEffects(ParseN
             goto restart;
         return true;
 
       // Function calls can invoke non-local code.
       case ParseNodeKind::New:
       case ParseNodeKind::Call:
       case ParseNodeKind::TaggedTemplate:
       case ParseNodeKind::SuperCall:
+        MOZ_ASSERT(pn->isArity(PN_BINARY));
+        *answer = true;
+        return true;
+
+      // Function arg lists can contain arbitrary expressions. Technically
+      // this only causes side-effects if one of the arguments does, but since
+      // the call being made will always trigger side-effects, it isn't needed.
+      case ParseNodeKind::Arguments:
         MOZ_ASSERT(pn->isArity(PN_LIST));
         *answer = true;
         return true;
 
       case ParseNodeKind::Pipeline:
         MOZ_ASSERT(pn->isArity(PN_LIST));
         MOZ_ASSERT(pn->pn_count >= 2);
         *answer = true;
@@ -1389,16 +1397,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       case ParseNodeKind::ImportSpecList: // by ParseNodeKind::Import
       case ParseNodeKind::ImportSpec:      // by ParseNodeKind::Import
       case ParseNodeKind::ExportBatchSpec:// by ParseNodeKind::Export
       case ParseNodeKind::ExportSpecList: // by ParseNodeKind::Export
       case ParseNodeKind::ExportSpec:      // by ParseNodeKind::Export
       case ParseNodeKind::CallSiteObj:      // by ParseNodeKind::TaggedTemplate
       case ParseNodeKind::PosHolder:        // by ParseNodeKind::NewTarget
       case ParseNodeKind::SuperBase:        // by ParseNodeKind::Elem and others
+      case ParseNodeKind::PropertyName:     // by ParseNodeKind::Dot
         MOZ_CRASH("handled by parent nodes");
 
       case ParseNodeKind::Limit: // invalid sentinel value
         MOZ_CRASH("invalid node kind");
     }
 
     MOZ_CRASH("invalid, unenumerated ParseNodeKind value encountered in "
               "BytecodeEmitter::checkSideEffects");
@@ -1860,49 +1869,49 @@ BytecodeEmitter::emitTDZCheckIfNeeded(JS
 }
 
 bool
 BytecodeEmitter::emitPropLHS(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
     MOZ_ASSERT(!pn->as<PropertyAccess>().isSuper());
 
-    ParseNode* pn2 = pn->pn_expr;
+    ParseNode* pn2 = pn->pn_left;
 
     /*
      * If the object operand is also a dotted property reference, reverse the
-     * list linked via pn_expr temporarily so we can iterate over it from the
+     * list linked via pn_left temporarily so we can iterate over it from the
      * bottom up (reversing again as we go), to avoid excessive recursion.
      */
     if (pn2->isKind(ParseNodeKind::Dot) && !pn2->as<PropertyAccess>().isSuper()) {
         ParseNode* pndot = pn2;
         ParseNode* pnup = nullptr;
         ParseNode* pndown;
         for (;;) {
-            /* Reverse pndot->pn_expr to point up, not down. */
-            pndown = pndot->pn_expr;
-            pndot->pn_expr = pnup;
+            /* Reverse pndot->pn_left to point up, not down. */
+            pndown = pndot->pn_left;
+            pndot->pn_left = pnup;
             if (!pndown->isKind(ParseNodeKind::Dot) || pndown->as<PropertyAccess>().isSuper())
                 break;
             pnup = pndot;
             pndot = pndown;
         }
 
         /* pndown is a primary expression, not a dotted property reference. */
         if (!emitTree(pndown))
             return false;
 
         do {
             /* Walk back up the list, emitting annotated name ops. */
-            if (!emitAtomOp(pndot, JSOP_GETPROP))
-                return false;
-
-            /* Reverse the pn_expr link again. */
-            pnup = pndot->pn_expr;
-            pndot->pn_expr = pndown;
+            if (!emitAtomOp(pndot->pn_right, JSOP_GETPROP))
+                return false;
+
+            /* Reverse the pn_left link again. */
+            pnup = pndot->pn_left;
+            pndot->pn_left = pndown;
             pndown = pndot;
         } while ((pndot = pnup) != nullptr);
         return true;
     }
 
     // The non-optimized case.
     return emitTree(pn2);
 }
@@ -1917,41 +1926,41 @@ BytecodeEmitter::emitSuperPropLHS(ParseN
     if (!emit1(JSOP_SUPERBASE))
         return false;
     return true;
 }
 
 bool
 BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
 {
-    MOZ_ASSERT(pn->isArity(PN_NAME));
+    MOZ_ASSERT(pn->isArity(PN_BINARY));
 
     if (!emitPropLHS(pn))
         return false;
 
     if (op == JSOP_CALLPROP && !emit1(JSOP_DUP))
         return false;
 
-    if (!emitAtomOp(pn, op))
+    if (!emitAtomOp(pn->pn_right, op))
         return false;
 
     if (op == JSOP_CALLPROP && !emit1(JSOP_SWAP))
         return false;
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitSuperGetProp(ParseNode* pn, bool isCall)
 {
     ParseNode* base = &pn->as<PropertyAccess>().expression();
     if (!emitSuperPropLHS(base, isCall))
         return false;
 
-    if (!emitAtomOp(pn, JSOP_GETPROP_SUPER))
+    if (!emitAtomOp(pn->pn_right, JSOP_GETPROP_SUPER))
         return false;
 
     if (isCall && !emit1(JSOP_SWAP))
         return false;
 
     return true;
 }
 
@@ -1971,17 +1980,17 @@ BytecodeEmitter::emitPropIncDec(ParseNod
         if (!emit1(JSOP_DUP2))                      // THIS OBJ THIS OBJ
             return false;
     } else {
         if (!emitPropLHS(pn->pn_kid))               // OBJ
             return false;
         if (!emit1(JSOP_DUP))                       // OBJ OBJ
             return false;
     }
-    if (!emitAtomOp(pn->pn_kid, isSuper? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V
+    if (!emitAtomOp(pn->pn_kid->pn_right, isSuper ? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V
         return false;
     if (!emit1(JSOP_POS))                           // OBJ N
         return false;
     if (post && !emit1(JSOP_DUP))                   // OBJ N? N
         return false;
     if (!emit1(JSOP_ONE))                           // OBJ N? N 1
         return false;
     if (!emit1(binop))                              // OBJ N? N+1
@@ -1997,17 +2006,17 @@ BytecodeEmitter::emitPropIncDec(ParseNod
                 return false;
             if (!emit1(JSOP_SWAP))                 // N THIS OBJ N+1
                 return false;
         }
     }
 
     JSOp setOp = isSuper ? sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
                          : sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
-    if (!emitAtomOp(pn->pn_kid, setOp))             // N? N+1
+    if (!emitAtomOp(pn->pn_kid->pn_right, setOp))   // N? N+1
         return false;
     if (post && !emit1(JSOP_POP))                   // RESULT
         return false;
 
     return true;
 }
 
 bool
@@ -2687,17 +2696,17 @@ BytecodeEmitter::emitDestructuringLHSRef
 
     switch (target->getKind()) {
       case ParseNodeKind::Dot: {
         if (target->as<PropertyAccess>().isSuper()) {
             if (!emitSuperPropLHS(&target->as<PropertyAccess>().expression()))
                 return false;
             *emitted = 2;
         } else {
-            if (!emitTree(target->pn_expr))
+            if (!emitTree(target->pn_left))
                 return false;
             *emitted = 1;
         }
         break;
       }
 
       case ParseNodeKind::Elem: {
         if (target->as<PropertyByValue>().isSuper()) {
@@ -2805,17 +2814,17 @@ BytecodeEmitter::emitSetOrInitializeDest
 
           case ParseNodeKind::Dot: {
             // The reference is already pushed by emitDestructuringLHSRef.
             JSOp setOp;
             if (target->as<PropertyAccess>().isSuper())
                 setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
             else
                 setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
-            if (!emitAtomOp(target, setOp))
+            if (!emitAtomOp(target->pn_right, setOp))
                 return false;
             break;
           }
 
           case ParseNodeKind::Elem: {
             // The reference is already pushed by emitDestructuringLHSRef.
             if (target->as<PropertyByValue>().isSuper()) {
                 JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER;
@@ -3918,21 +3927,21 @@ BytecodeEmitter::emitAssignment(ParseNod
 
     switch (lhs->getKind()) {
       case ParseNodeKind::Dot:
         if (lhs->as<PropertyAccess>().isSuper()) {
             if (!emitSuperPropLHS(&lhs->as<PropertyAccess>().expression()))
                 return false;
             offset += 2;
         } else {
-            if (!emitTree(lhs->expr()))
+            if (!emitTree(lhs->pn_left))
                 return false;
             offset += 1;
         }
-        if (!makeAtomIndex(lhs->pn_atom, &atomIndex))
+        if (!makeAtomIndex(lhs->pn_right->pn_atom, &atomIndex))
             return false;
         break;
       case ParseNodeKind::Elem: {
         MOZ_ASSERT(lhs->isArity(PN_BINARY));
         EmitElemOption opt = op == JSOP_NOP ? EmitElemOption::Get : EmitElemOption::CompoundAssign;
         if (lhs->as<PropertyByValue>().isSuper()) {
             if (!emitSuperElemOperands(lhs, opt))
                 return false;
@@ -3971,17 +3980,17 @@ BytecodeEmitter::emitAssignment(ParseNod
             JSOp getOp;
             if (lhs->as<PropertyAccess>().isSuper()) {
                 if (!emit1(JSOP_DUP2))
                     return false;
                 getOp = JSOP_GETPROP_SUPER;
             } else {
                 if (!emit1(JSOP_DUP))
                     return false;
-                bool isLength = (lhs->pn_atom == cx->names().length);
+                bool isLength = (lhs->pn_right->pn_atom == cx->names().length);
                 getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP;
             }
             if (!emitIndex32(getOp, atomIndex))
                 return false;
             break;
           }
           case ParseNodeKind::Elem: {
             JSOp elemOp;
@@ -4348,22 +4357,20 @@ BytecodeEmitter::emitTry(ParseNode* pn)
     return true;
 }
 
 bool
 BytecodeEmitter::emitIf(ParseNode* pn)
 {
     IfEmitter ifThenElse(this);
 
+    if (!ifThenElse.emitIf(Some(pn->pn_pos.begin)))
+        return false;
+
   if_again:
-    // Make sure this code is attributed to the "if" so that it gets a useful
-    // column number, instead of the default 0 value.
-    if (!updateSourceCoordNotes(pn->pn_pos.begin))
-        return false;
-
     /* Emit code for the condition before pushing stmtInfo. */
     if (!emitTree(pn->pn_kid1))
         return false;
 
     ParseNode* elseNode = pn->pn_kid3;
     if (elseNode) {
         if (!ifThenElse.emitThenElse())
             return false;
@@ -4375,17 +4382,17 @@ BytecodeEmitter::emitIf(ParseNode* pn)
     /* Emit code for the then part. */
     if (!emitTree(pn->pn_kid2))
         return false;
 
     if (elseNode) {
         if (elseNode->isKind(ParseNodeKind::If)) {
             pn = elseNode;
 
-            if (!ifThenElse.emitElseIf())
+            if (!ifThenElse.emitElseIf(Some(pn->pn_pos.begin)))
                 return false;
 
             goto if_again;
         }
 
         if (!ifThenElse.emitElse())
             return false;
 
@@ -4816,17 +4823,17 @@ BytecodeEmitter::emitForOf(ParseNode* fo
 
     ParseNode* forHeadExpr = forOfHead->pn_kid3;
 
     // Certain builtins (e.g. Array.from) are implemented in self-hosting
     // as for-of loops.
     bool allowSelfHostedIter = false;
     if (emitterMode == BytecodeEmitter::SelfHosting &&
         forHeadExpr->isKind(ParseNodeKind::Call) &&
-        forHeadExpr->pn_head->name() == cx->names().allowContentIter)
+        forHeadExpr->pn_left->name() == cx->names().allowContentIter)
     {
         allowSelfHostedIter = true;
     }
 
     ForOfEmitter forOf(this, headLexicalEmitterScope, allowSelfHostedIter, iterKind);
 
     if (!forOf.emitIterated())                            //
         return false;
@@ -6137,45 +6144,47 @@ BytecodeEmitter::emitSelfHostedCallFunct
     // invokes the callee with the correct |this| object and arguments.
     // callFunction(fun, thisArg, arg0, arg1) thus becomes:
     // - emit lookup for fun
     // - emit lookup for thisArg
     // - emit lookups for arg0, arg1
     //
     // argc is set to the amount of actually emitted args and the
     // emitting of args below is disabled by setting emitArgs to false.
-    ParseNode* pn2 = pn->pn_head;
-    const char* errorName = SelfHostedCallFunctionName(pn2->name(), cx);
-
-    if (pn->pn_count < 3) {
+    ParseNode* pn_callee = pn->pn_left;
+    ParseNode* pn_args = pn->pn_right;
+
+    const char* errorName = SelfHostedCallFunctionName(pn_callee->name(), cx);
+
+    if (pn_args->pn_count < 2) {
         reportError(pn, JSMSG_MORE_ARGS_NEEDED, errorName, "2", "s");
         return false;
     }
 
     JSOp callOp = pn->getOp();
     if (callOp != JSOP_CALL) {
         reportError(pn, JSMSG_NOT_CONSTRUCTOR, errorName);
         return false;
     }
 
-    bool constructing = pn2->name() == cx->names().constructContentFunction;
-    ParseNode* funNode = pn2->pn_next;
+    bool constructing = pn_callee->name() == cx->names().constructContentFunction;
+    ParseNode* funNode = pn_args->pn_head;
     if (constructing) {
         callOp = JSOP_NEW;
     } else if (funNode->getKind() == ParseNodeKind::Name &&
                funNode->name() == cx->names().std_Function_apply) {
         callOp = JSOP_FUNAPPLY;
     }
 
     if (!emitTree(funNode))
         return false;
 
 #ifdef DEBUG
     if (emitterMode == BytecodeEmitter::SelfHosting &&
-        pn2->name() == cx->names().callFunction)
+        pn_callee->name() == cx->names().callFunction)
     {
         if (!emit1(JSOP_DEBUGCHECKSELFHOSTED))
             return false;
     }
 #endif
 
     ParseNode* thisOrNewTarget = funNode->pn_next;
     if (constructing) {
@@ -6194,36 +6203,36 @@ BytecodeEmitter::emitSelfHostedCallFunct
             return false;
     }
 
     if (constructing) {
         if (!emitTree(thisOrNewTarget))
             return false;
     }
 
-    uint32_t argc = pn->pn_count - 3;
+    uint32_t argc = pn_args->pn_count - 2;
     if (!emitCall(callOp, argc))
         return false;
 
     checkTypeSet(callOp);
     return true;
 }
 
 bool
 BytecodeEmitter::emitSelfHostedResumeGenerator(ParseNode* pn)
 {
+    ParseNode* pn_args = pn->pn_right;
+
     // Syntax: resumeGenerator(gen, value, 'next'|'throw'|'return')
-    if (pn->pn_count != 4) {
+    if (pn_args->pn_count != 3) {
         reportError(pn, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s");
         return false;
     }
 
-    ParseNode* funNode = pn->pn_head;  // The resumeGenerator node.
-
-    ParseNode* genNode = funNode->pn_next;
+    ParseNode* genNode = pn_args->pn_head;
     if (!emitTree(genNode))
         return false;
 
     ParseNode* valNode = genNode->pn_next;
     if (!emitTree(valNode))
         return false;
 
     ParseNode* kindNode = valNode->pn_next;
@@ -6245,34 +6254,36 @@ BytecodeEmitter::emitSelfHostedForceInte
     if (!emit1(JSOP_UNDEFINED))
         return false;
     return true;
 }
 
 bool
 BytecodeEmitter::emitSelfHostedAllowContentIter(ParseNode* pn)
 {
-    if (pn->pn_count != 2) {
+    ParseNode* pn_args = pn->pn_right;
+
+    if (pn_args->pn_count != 1) {
         reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentIter", "1", "");
         return false;
     }
 
     // We're just here as a sentinel. Pass the value through directly.
-    return emitTree(pn->pn_head->pn_next);
+    return emitTree(pn_args->pn_head);
 }
 
 bool
 BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn)
 {
-    // Only optimize when 3 arguments are passed (we use 4 to include |this|).
-    MOZ_ASSERT(pn->pn_count == 4);
-
-    ParseNode* funNode = pn->pn_head;  // The _DefineDataProperty node.
-
-    ParseNode* objNode = funNode->pn_next;
+    ParseNode* pn_args = pn->pn_right;
+
+    // Only optimize when 3 arguments are passed.
+    MOZ_ASSERT(pn_args->pn_count == 3);
+
+    ParseNode* objNode = pn_args->pn_head;
     if (!emitTree(objNode))
         return false;
 
     ParseNode* idNode = objNode->pn_next;
     if (!emitTree(idNode))
         return false;
 
     ParseNode* valNode = idNode->pn_next;
@@ -6283,45 +6294,45 @@ BytecodeEmitter::emitSelfHostedDefineDat
     // but that's fine because the self-hosted code doesn't use the return
     // value.
     return emit1(JSOP_INITELEM);
 }
 
 bool
 BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn)
 {
-    if (pn->pn_count != 3) {
+    ParseNode* pn_args = pn->pn_right;
+
+    if (pn_args->pn_count != 2) {
         reportError(pn, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", "");
         return false;
     }
 
-    ParseNode* funNode = pn->pn_head;  // The hasOwn node.
-
-    ParseNode* idNode = funNode->pn_next;
+    ParseNode* idNode = pn_args->pn_head;
     if (!emitTree(idNode))
         return false;
 
     ParseNode* objNode = idNode->pn_next;
     if (!emitTree(objNode))
         return false;
 
     return emit1(JSOP_HASOWN);
 }
 
 bool
 BytecodeEmitter::emitSelfHostedGetPropertySuper(ParseNode* pn)
 {
-    if (pn->pn_count != 4) {
+    ParseNode* pn_args = pn->pn_right;
+
+    if (pn_args->pn_count != 3) {
         reportError(pn, JSMSG_MORE_ARGS_NEEDED, "getPropertySuper", "3", "");
         return false;
     }
 
-    ParseNode* funNode = pn->pn_head;  // The getPropertySuper node.
-
-    ParseNode* objNode = funNode->pn_next;
+    ParseNode* objNode = pn_args->pn_head;
     ParseNode* idNode = objNode->pn_next;
     ParseNode* receiverNode = idNode->pn_next;
 
     if (!emitTree(receiverNode))
         return false;
 
     if (!emitTree(idNode))
         return false;
@@ -6340,21 +6351,21 @@ BytecodeEmitter::isRestParameter(ParseNo
 
     FunctionBox* funbox = sc->asFunctionBox();
     RootedFunction fun(cx, funbox->function());
     if (!funbox->hasRest())
         return false;
 
     if (!pn->isKind(ParseNodeKind::Name)) {
         if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(ParseNodeKind::Call)) {
-            ParseNode* pn2 = pn->pn_head;
-            if (pn2->getKind() == ParseNodeKind::Name &&
-                pn2->name() == cx->names().allowContentIter)
+            ParseNode* pn_callee = pn->pn_left;
+            if (pn_callee->getKind() == ParseNodeKind::Name &&
+                pn_callee->name() == cx->names().allowContentIter)
             {
-                return isRestParameter(pn2->pn_next);
+                return isRestParameter(pn->pn_right->pn_head);
             }
         }
         return false;
     }
 
     JSAtom* name = pn->name();
     Maybe<NameLocation> paramLoc = locationOfNameBoundInFunctionScope(name);
     if (paramLoc && lookupName(name) == *paramLoc) {
@@ -6474,111 +6485,32 @@ BytecodeEmitter::emitPipeline(ParseNode*
 
         checkTypeSet(JSOP_CALL);
     } while ((callee = callee->pn_next));
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
-{
-    bool callop =
-        pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate);
-    /*
-     * Emit callable invocation or operator new (constructor call) code.
-     * First, emit code for the left operand to evaluate the callable or
-     * constructable object expression.
-     *
-     * For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc.
-     * This is necessary to interpose the lambda-initialized method read
-     * barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by
-     * JSOP_{SET,INIT}PROP.
-     *
-     * Then (or in a call case that has no explicit reference-base
-     * object) we emit JSOP_UNDEFINED to produce the undefined |this|
-     * value required for calls (which non-strict mode functions
-     * will box into the global object).
-     */
-    uint32_t argc = pn->pn_count - 1;
+BytecodeEmitter::emitArguments(ParseNode* pn, bool callop, bool spread)
+{
+    uint32_t argc = pn->pn_count;
 
     if (argc >= ARGC_LIMIT) {
         reportError(pn, callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
         return false;
     }
 
-    ParseNode* pn2 = pn->pn_head;
-    bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
-
-    if (pn2->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
-        // Calls to "forceInterpreter", "callFunction",
-        // "callContentFunction", or "resumeGenerator" in self-hosted
-        // code generate inline bytecode.
-        if (pn2->name() == cx->names().callFunction ||
-            pn2->name() == cx->names().callContentFunction ||
-            pn2->name() == cx->names().constructContentFunction)
-        {
-            return emitSelfHostedCallFunction(pn);
-        }
-        if (pn2->name() == cx->names().resumeGenerator)
-            return emitSelfHostedResumeGenerator(pn);
-        if (pn2->name() == cx->names().forceInterpreter)
-            return emitSelfHostedForceInterpreter();
-        if (pn2->name() == cx->names().allowContentIter)
-            return emitSelfHostedAllowContentIter(pn);
-        if (pn2->name() == cx->names().defineDataPropertyIntrinsic && pn->pn_count == 4)
-            return emitSelfHostedDefineDataProperty(pn);
-        if (pn2->name() == cx->names().hasOwn)
-            return emitSelfHostedHasOwn(pn);
-        if (pn2->name() == cx->names().getPropertySuper)
-            return emitSelfHostedGetPropertySuper(pn);
-        // Fall through
-    }
-
-    if (!emitCallee(pn2, pn, &callop))
-        return false;
-
-    bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW ||
-                   pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL;
-
-
-    // Emit room for |this|.
-    if (!callop) {
-        if (isNewOp) {
-            if (!emit1(JSOP_IS_CONSTRUCTING))
-                return false;
-        } else {
-            if (!emit1(JSOP_UNDEFINED))
-                return false;
-        }
-    }
-
-    /*
-     * Emit code for each argument in order, then emit the JSOP_*CALL or
-     * JSOP_NEW bytecode with a two-byte immediate telling how many args
-     * were pushed on the operand stack.
-     */
     if (!spread) {
-        for (ParseNode* pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
+        for (ParseNode* pn3 = pn->pn_head; pn3; pn3 = pn3->pn_next) {
             if (!emitTree(pn3))
                 return false;
         }
-
-        if (isNewOp) {
-            if (pn->isKind(ParseNodeKind::SuperCall)) {
-                if (!emit1(JSOP_NEWTARGET))
-                    return false;
-            } else {
-                // Repush the callee as new.target
-                if (!emitDupAt(argc + 1))
-                    return false;
-            }
-        }
     } else {
-        ParseNode* args = pn2->pn_next;
+        ParseNode* args = pn->pn_head;
         bool emitOptCode = (argc == 1) && isRestParameter(args->pn_kid);
         InternalIfEmitter ifNotOptimizable(this);
 
         if (emitOptCode) {
             // Emit a preparation code to optimize the spread call with a rest
             // parameter:
             //
             //   function f(...args) {
@@ -6608,39 +6540,169 @@ BytecodeEmitter::emitCallOrNew(ParseNode
 
         if (!emitArray(args, argc))
             return false;
 
         if (emitOptCode) {
             if (!ifNotOptimizable.emitEnd())
                 return false;
         }
-
+    }
+
+    return true;
+}
+
+bool
+BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
+{
+    bool callop =
+        pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate);
+
+    /*
+     * Emit callable invocation or operator new (constructor call) code.
+     * First, emit code for the left operand to evaluate the callable or
+     * constructable object expression.
+     *
+     * For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc.
+     * This is necessary to interpose the lambda-initialized method read
+     * barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by
+     * JSOP_{SET,INIT}PROP.
+     *
+     * Then (or in a call case that has no explicit reference-base
+     * object) we emit JSOP_UNDEFINED to produce the undefined |this|
+     * value required for calls (which non-strict mode functions
+     * will box into the global object).
+     */
+    ParseNode* pn_callee = pn->pn_left;
+    ParseNode* pn_args = pn->pn_right;
+
+    bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
+
+    if (pn_callee->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
+        // Calls to "forceInterpreter", "callFunction",
+        // "callContentFunction", or "resumeGenerator" in self-hosted
+        // code generate inline bytecode.
+        if (pn_callee->name() == cx->names().callFunction ||
+            pn_callee->name() == cx->names().callContentFunction ||
+            pn_callee->name() == cx->names().constructContentFunction)
+        {
+            return emitSelfHostedCallFunction(pn);
+        }
+        if (pn_callee->name() == cx->names().resumeGenerator)
+            return emitSelfHostedResumeGenerator(pn);
+        if (pn_callee->name() == cx->names().forceInterpreter)
+            return emitSelfHostedForceInterpreter();
+        if (pn_callee->name() == cx->names().allowContentIter)
+            return emitSelfHostedAllowContentIter(pn);
+        if (pn_callee->name() == cx->names().defineDataPropertyIntrinsic && pn_args->pn_count == 3)
+            return emitSelfHostedDefineDataProperty(pn);
+        if (pn_callee->name() == cx->names().hasOwn)
+            return emitSelfHostedHasOwn(pn);
+        if (pn_callee->name() == cx->names().getPropertySuper)
+            return emitSelfHostedGetPropertySuper(pn);
+        // Fall through
+    }
+
+    if (!emitCallee(pn_callee, pn, &callop))
+        return false;
+
+    bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW ||
+                   pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL;
+
+    // Emit room for |this|.
+    if (!callop) {
         if (isNewOp) {
-            if (pn->isKind(ParseNodeKind::SuperCall)) {
-                if (!emit1(JSOP_NEWTARGET))
-                    return false;
-            } else {
-                if (!emitDupAt(2))
-                    return false;
+            if (!emit1(JSOP_IS_CONSTRUCTING))
+                return false;
+        } else {
+            if (!emit1(JSOP_UNDEFINED))
+                return false;
+        }
+    }
+
+    if (!emitArguments(pn_args, callop, spread))
+        return false;
+
+    uint32_t argc = pn_args->pn_count;
+
+    /*
+     * Emit code for each argument in order, then emit the JSOP_*CALL or
+     * JSOP_NEW bytecode with a two-byte immediate telling how many args
+     * were pushed on the operand stack.
+     */
+    if (isNewOp) {
+        if (pn->isKind(ParseNodeKind::SuperCall)) {
+            if (!emit1(JSOP_NEWTARGET))
+                return false;
+        } else if (!spread) {
+            // Repush the callee as new.target
+            if (!emitDupAt(argc + 1))
+                return false;
+        } else {
+            if (!emitDupAt(2))
+                return false;
+        }
+    }
+
+    ParseNode* coordNode = pn;
+    if (pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL)) {
+        switch (pn_callee->getKind()) {
+          case ParseNodeKind::Dot: {
+
+            // Check if this member is a simple chain of simple chain of
+            // property accesses, e.g. x.y.z, this.x.y, super.x.y
+            bool simpleDotChain = false;
+            for (ParseNode* cur = pn_callee; cur->isKind(ParseNodeKind::Dot); cur = cur->pn_left) {
+                ParseNode* left = cur->pn_left;
+                if (left->isKind(ParseNodeKind::Name) || left->isKind(ParseNodeKind::This) ||
+                    left->isKind(ParseNodeKind::SuperBase))
+                {
+                    simpleDotChain = true;
+                }
             }
+
+            if (!simpleDotChain) {
+                // obj().aprop() // expression
+                //       ^       // column coord
+                //
+                // Note: Because of the constant folding logic in FoldElement,
+                // this case also applies for constant string properties.
+                //
+                // obj()['aprop']() // expression
+                //       ^          // column coord
+                coordNode = pn_callee->pn_right;
+            }
+            break;
+          }
+          case ParseNodeKind::Elem:
+            // obj[expr]() // expression
+            //          ^  // column coord
+            coordNode = pn_args;
+            break;
+          default:
+            break;
         }
     }
 
     if (!spread) {
         if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
-            if (!emitCall(JSOP_CALL_IGNORES_RV, argc, pn))
+            if (!emitCall(JSOP_CALL_IGNORES_RV, argc, coordNode))
                 return false;
             checkTypeSet(JSOP_CALL_IGNORES_RV);
         } else {
-            if (!emitCall(pn->getOp(), argc, pn))
+            if (!emitCall(pn->getOp(), argc, coordNode))
                 return false;
             checkTypeSet(pn->getOp());
         }
     } else {
+        if (coordNode) {
+            if (!updateSourceCoordNotes(coordNode->pn_pos.begin))
+                return false;
+        }
+
         if (!emit1(pn->getOp()))
             return false;
         checkTypeSet(pn->getOp());
     }
     if (pn->isOp(JSOP_EVAL) ||
         pn->isOp(JSOP_STRICTEVAL) ||
         pn->isOp(JSOP_SPREADEVAL) ||
         pn->isOp(JSOP_STRICTSPREADEVAL))
@@ -6841,36 +6903,38 @@ BytecodeEmitter::emitLabeledStatement(co
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional,
                                            ValueUsage valueUsage /* = ValueUsage::WantValue */)
 {
-    /* Emit the condition, then branch if false to the else part. */
+    CondEmitter cond(this);
+    if (!cond.emitCond())
+        return false;
+
     if (!emitTree(&conditional.condition()))
         return false;
 
-    IfEmitter ifThenElse(this);
-    if (!ifThenElse.emitCond())
+    if (!cond.emitThenElse())
         return false;
 
     if (!emitTree(&conditional.thenExpression(), valueUsage))
         return false;
 
-    if (!ifThenElse.emitElse())
+    if (!cond.emitElse())
         return false;
 
     if (!emitTree(&conditional.elseExpression(), valueUsage))
         return false;
 
-    if (!ifThenElse.emitEnd())
-        return false;
-    MOZ_ASSERT(ifThenElse.pushed() == 1);
+    if (!cond.emitEnd())
+        return false;
+    MOZ_ASSERT(cond.pushed() == 1);
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, PropListType type)
 {
     for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
@@ -7199,17 +7263,17 @@ BytecodeEmitter::emitArray(ParseNode* pn
                 return false;
         } else {
             ParseNode* expr;
             if (pn2->isKind(ParseNodeKind::Spread)) {
                 expr = pn2->pn_kid;
 
                 if (emitterMode == BytecodeEmitter::SelfHosting &&
                     expr->isKind(ParseNodeKind::Call) &&
-                    expr->pn_head->name() == cx->names().allowContentIter)
+                    expr->pn_left->name() == cx->names().allowContentIter)
                 {
                     allowSelfHostedIter = true;
                 }
             } else {
                 expr = pn2;
             }
             if (!emitTree(expr))                                         // ARRAY INDEX? VALUE
                 return false;
@@ -8247,18 +8311,19 @@ BytecodeEmitter::emitTree(ParseNode* pn,
             return false;
         break;
 
       case ParseNodeKind::SetThis:
         if (!emitSetThis(pn))
             return false;
         break;
 
+      case ParseNodeKind::PropertyName:
       case ParseNodeKind::PosHolder:
-        MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder");
+        MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder or ::Property");
 
       default:
         MOZ_ASSERT(0);
     }
 
     /* bce->emitLevel == 1 means we're last on the stack, so finish up. */
     if (emitLevel == 1) {
         if (!updateSourceCoordNotes(pn->pn_pos.end))
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -803,16 +803,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
 
     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn);
 
     MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional,
                                                 ValueUsage valueUsage = ValueUsage::WantValue);
 
     bool isRestParameter(ParseNode* pn);
 
+    MOZ_MUST_USE bool emitArguments(ParseNode* pn, bool callop, bool spread);
     MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
     MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedForceInterpreter();
     MOZ_MUST_USE bool emitSelfHostedAllowContentIter(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedDefineDataProperty(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedGetPropertySuper(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedHasOwn(ParseNode* pn);
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -342,18 +342,20 @@ ContainsHoistedDeclaration(JSContext* cx
       case ParseNodeKind::UrshAssign:
       case ParseNodeKind::MulAssign:
       case ParseNodeKind::DivAssign:
       case ParseNodeKind::ModAssign:
       case ParseNodeKind::PowAssign:
       case ParseNodeKind::Comma:
       case ParseNodeKind::Array:
       case ParseNodeKind::Object:
+      case ParseNodeKind::PropertyName:
       case ParseNodeKind::Dot:
       case ParseNodeKind::Elem:
+      case ParseNodeKind::Arguments:
       case ParseNodeKind::Call:
       case ParseNodeKind::Name:
       case ParseNodeKind::TemplateString:
       case ParseNodeKind::TemplateStringList:
       case ParseNodeKind::TaggedTemplate:
       case ParseNodeKind::CallSiteObj:
       case ParseNodeKind::String:
       case ParseNodeKind::RegExp:
@@ -1248,17 +1250,20 @@ FoldElement(JSContext* cx, ParseNode** n
     }
 
     // If we don't have a name, we can't optimize to getprop.
     if (!name)
         return true;
 
     // Optimization 3: We have expr["foo"] where foo is not an index.  Convert
     // to a property access (like expr.foo) that optimizes better downstream.
-    ParseNode* dottedAccess = parser.newPropertyAccess(expr, name, node->pn_pos.end);
+    ParseNode* nameNode = parser.newPropertyName(name, key->pn_pos);
+    if (!nameNode)
+        return false;
+    ParseNode* dottedAccess = parser.newPropertyAccess(expr, nameNode);
     if (!dottedAccess)
         return false;
     dottedAccess->setInParens(node->isInParens());
     ReplaceNode(nodePtr, dottedAccess);
 
     return true;
 }
 
@@ -1404,34 +1409,51 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
     return true;
 }
 
 static bool
 FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Call) ||
                node->isKind(ParseNodeKind::SuperCall) ||
+               node->isKind(ParseNodeKind::New) ||
                node->isKind(ParseNodeKind::TaggedTemplate));
-    MOZ_ASSERT(node->isArity(PN_LIST));
+    MOZ_ASSERT(node->isArity(PN_BINARY));
 
     // Don't fold a parenthesized callable component in an invocation, as this
     // might cause a different |this| value to be used, changing semantics:
     //
     //   var prop = "global";
     //   var obj = { prop: "obj", f: function() { return this.prop; } };
     //   assertEq((true ? obj.f : null)(), "global");
     //   assertEq(obj.f(), "obj");
     //   assertEq((true ? obj.f : null)``, "global");
     //   assertEq(obj.f``, "obj");
     //
     // See bug 537673 and bug 1182373.
+    ParseNode** pn_callee = &node->pn_left;
+    if (node->isKind(ParseNodeKind::New) || !(*pn_callee)->isInParens()) {
+        if (!Fold(cx, pn_callee, parser))
+            return false;
+    }
+
+    ParseNode** pn_args = &node->pn_right;
+    if (!Fold(cx, pn_args, parser))
+        return false;
+
+    return true;
+}
+
+static bool
+FoldArguments(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
+{
+    MOZ_ASSERT(node->isKind(ParseNodeKind::Arguments));
+    MOZ_ASSERT(node->isArity(PN_LIST));
+
     ParseNode** listp = &node->pn_head;
-    if ((*listp)->isInParens())
-        listp = &(*listp)->pn_next;
-
     for (; *listp; listp = &(*listp)->pn_next) {
         if (!Fold(cx, listp, parser))
             return false;
     }
 
     // If the last node in the list was replaced, pn_tail points into the wrong node.
     node->pn_tail = listp;
 
@@ -1477,24 +1499,24 @@ FoldForHead(JSContext* cx, ParseNode* no
 
     return true;
 }
 
 static bool
 FoldDottedProperty(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Dot));
-    MOZ_ASSERT(node->isArity(PN_NAME));
+    MOZ_ASSERT(node->isArity(PN_BINARY));
 
     // Iterate through a long chain of dotted property accesses to find the
     // most-nested non-dotted property node, then fold that.
-    ParseNode** nested = &node->pn_expr;
+    ParseNode** nested = &node->pn_left;
     while ((*nested)->isKind(ParseNodeKind::Dot)) {
-        MOZ_ASSERT((*nested)->isArity(PN_NAME));
-        nested = &(*nested)->pn_expr;
+        MOZ_ASSERT((*nested)->isArity(PN_BINARY));
+        nested = &(*nested)->pn_left;
     }
 
     return Fold(cx, nested, parser);
 }
 
 static bool
 FoldName(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
@@ -1637,17 +1659,16 @@ Fold(JSContext* cx, ParseNode** pnp, Per
       case ParseNodeKind::Ne:
       case ParseNodeKind::Lt:
       case ParseNodeKind::Le:
       case ParseNodeKind::Gt:
       case ParseNodeKind::Ge:
       case ParseNodeKind::InstanceOf:
       case ParseNodeKind::In:
       case ParseNodeKind::Comma:
-      case ParseNodeKind::New:
       case ParseNodeKind::Array:
       case ParseNodeKind::Object:
       case ParseNodeKind::StatementList:
       case ParseNodeKind::ClassMethodList:
       case ParseNodeKind::TemplateStringList:
       case ParseNodeKind::Var:
       case ParseNodeKind::Const:
       case ParseNodeKind::Let:
@@ -1689,20 +1710,24 @@ Fold(JSContext* cx, ParseNode** pnp, Per
 
       case ParseNodeKind::Elem:
         return FoldElement(cx, pnp, parser);
 
       case ParseNodeKind::Add:
         return FoldAdd(cx, pnp, parser);
 
       case ParseNodeKind::Call:
+      case ParseNodeKind::New:
       case ParseNodeKind::SuperCall:
       case ParseNodeKind::TaggedTemplate:
         return FoldCall(cx, pn, parser);
 
+      case ParseNodeKind::Arguments:
+        return FoldArguments(cx, pn, parser);
+
       case ParseNodeKind::Switch:
       case ParseNodeKind::Colon:
       case ParseNodeKind::Assign:
       case ParseNodeKind::AddAssign:
       case ParseNodeKind::SubAssign:
       case ParseNodeKind::BitOrAssign:
       case ParseNodeKind::BitAndAssign:
       case ParseNodeKind::BitXorAssign:
@@ -1772,16 +1797,19 @@ Fold(JSContext* cx, ParseNode** pnp, Per
 
       case ParseNodeKind::ForHead:
         return FoldForHead(cx, pn, parser);
 
       case ParseNodeKind::Label:
         MOZ_ASSERT(pn->isArity(PN_NAME));
         return Fold(cx, &pn->pn_expr, parser);
 
+      case ParseNodeKind::PropertyName:
+        MOZ_CRASH("unreachable, handled by ::Dot");
+
       case ParseNodeKind::Dot:
         return FoldDottedProperty(cx, pn, parser);
 
       case ParseNodeKind::LexicalScope:
         MOZ_ASSERT(pn->isArity(PN_SCOPE));
         if (!pn->scopeBody())
             return true;
         return Fold(cx, &pn->pn_u.scope.body, parser);
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -270,26 +270,30 @@ class FullParseHandler
     void addArrayElement(ParseNode* literal, ParseNode* element) {
         MOZ_ASSERT(literal->isArity(PN_LIST));
 
         if (!element->isConstant())
             literal->pn_xflags |= PNX_NONCONST;
         addList(/* list = */ literal, /* child = */ element);
     }
 
-    ParseNode* newCall(const TokenPos& pos) {
-        return new_<ListNode>(ParseNodeKind::Call, JSOP_CALL, pos);
+    ParseNode* newCall(ParseNode* callee, ParseNode* args) {
+        return new_<BinaryNode>(ParseNodeKind::Call, JSOP_CALL, callee, args);
     }
 
-    ParseNode* newSuperCall(ParseNode* callee) {
-        return new_<ListNode>(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee);
+    ParseNode* newArguments(const TokenPos& pos) {
+        return new_<ListNode>(ParseNodeKind::Arguments, JSOP_NOP, pos);
     }
 
-    ParseNode* newTaggedTemplate(const TokenPos& pos) {
-        return new_<ListNode>(ParseNodeKind::TaggedTemplate, JSOP_CALL, pos);
+    ParseNode* newSuperCall(ParseNode* callee, ParseNode* args) {
+        return new_<BinaryNode>(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee, args);
+    }
+
+    ParseNode* newTaggedTemplate(ParseNode* tag, ParseNode* args) {
+        return new_<BinaryNode>(ParseNodeKind::TaggedTemplate, JSOP_CALL, tag, args);
     }
 
     ParseNode* newObjectLiteral(uint32_t begin) {
         return new_<ListNode>(ParseNodeKind::Object, TokenPos(begin, begin + 1));
     }
 
     ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock,
                         const TokenPos& pos)
@@ -655,18 +659,22 @@ class FullParseHandler
         TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end);
         return new_<TernaryNode>(ParseNodeKind::Try, body, catchScope, finallyBlock, pos);
     }
 
     ParseNode* newDebuggerStatement(const TokenPos& pos) {
         return new_<DebuggerStatement>(pos);
     }
 
-    ParseNode* newPropertyAccess(ParseNode* expr, PropertyName* key, uint32_t end) {
-        return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, end);
+    ParseNode* newPropertyName(PropertyName* name, const TokenPos& pos) {
+        return new_<NameNode>(ParseNodeKind::PropertyName, JSOP_NOP, name, pos);
+    }
+
+    ParseNode* newPropertyAccess(ParseNode* expr, ParseNode* key) {
+        return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, key->pn_pos.end);
     }
 
     ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) {
         return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
     }
 
     bool setupCatchScope(ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchBody) {
         ParseNode* catchpn;
@@ -730,23 +738,18 @@ class FullParseHandler
     ParseNode* newModule(const TokenPos& pos) {
         return new_<CodeNode>(ParseNodeKind::Module, JSOP_NOP, pos);
     }
 
     ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) {
         return new_<LexicalScopeNode>(bindings, body);
     }
 
-    Node newNewExpression(uint32_t begin, ParseNode* ctor) {
-        ParseNode* newExpr = new_<ListNode>(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, begin + 1));
-        if (!newExpr)
-            return nullptr;
-
-        addList(/* list = */ newExpr, /* child = */ ctor);
-        return newExpr;
+    Node newNewExpression(uint32_t begin, ParseNode* ctor, ParseNode* args) {
+        return new_<BinaryNode>(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, args->pn_pos.end), ctor, args);
     }
 
     ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs) {
         if (kind == ParseNodeKind::Assign && lhs->isKind(ParseNodeKind::Name) &&
             !lhs->isInParens())
         {
             checkAndSetIsDirectRHSAnonFunction(rhs);
         }
--- a/js/src/frontend/IfEmitter.cpp
+++ b/js/src/frontend/IfEmitter.cpp
@@ -8,37 +8,34 @@
 
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/SourceNotes.h"
 #include "vm/Opcodes.h"
 
 using namespace js;
 using namespace js::frontend;
 
-IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind)
+using mozilla::Maybe;
+
+BranchEmitterBase::BranchEmitterBase(BytecodeEmitter* bce, Kind kind)
   : bce_(bce),
-    thenDepth_(0),
     kind_(kind)
-#ifdef DEBUG
-  , pushed_(0),
-    calculatedPushed_(false),
-    state_(State::Start)
-#endif
+{}
+
+IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind)
+  : BranchEmitterBase(bce, kind)
 {}
 
 IfEmitter::IfEmitter(BytecodeEmitter* bce)
   : IfEmitter(bce, Kind::MayContainLexicalAccessInBranch)
 {}
 
 bool
-IfEmitter::emitIfInternal(SrcNoteType type)
+BranchEmitterBase::emitThenInternal(SrcNoteType type)
 {
-    MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
-    MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
-
     // The end of TDZCheckCache for cond for else-if.
     if (kind_ == Kind::MayContainLexicalAccessInBranch)
         tdzCache_.reset();
 
     // Emit an annotated branch-if-false around the then part.
     if (!bce_->newSrcNote(type))
         return false;
     if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_))
@@ -56,69 +53,30 @@ IfEmitter::emitIfInternal(SrcNoteType ty
     // Enclose then-branch with TDZCheckCache.
     if (kind_ == Kind::MayContainLexicalAccessInBranch)
         tdzCache_.emplace(bce_);
 
     return true;
 }
 
 void
-IfEmitter::calculateOrCheckPushed()
+BranchEmitterBase::calculateOrCheckPushed()
 {
 #ifdef DEBUG
     if (!calculatedPushed_) {
         pushed_ = bce_->stackDepth - thenDepth_;
         calculatedPushed_ = true;
     } else {
         MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_);
     }
 #endif
 }
 
 bool
-IfEmitter::emitThen()
-{
-    MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
-    if (!emitIfInternal(SRC_IF))
-        return false;
-
-#ifdef DEBUG
-    state_ = State::Then;
-#endif
-    return true;
-}
-
-bool
-IfEmitter::emitCond()
-{
-    MOZ_ASSERT(state_ == State::Start);
-    if (!emitIfInternal(SRC_COND))
-        return false;
-
-#ifdef DEBUG
-    state_ = State::Cond;
-#endif
-    return true;
-}
-
-bool
-IfEmitter::emitThenElse()
-{
-    MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
-    if (!emitIfInternal(SRC_IF_ELSE))
-        return false;
-
-#ifdef DEBUG
-    state_ = State::ThenElse;
-#endif
-    return true;
-}
-
-bool
-IfEmitter::emitElseInternal()
+BranchEmitterBase::emitElseInternal()
 {
     calculateOrCheckPushed();
 
     // The end of TDZCheckCache for then-clause.
     if (kind_ == Kind::MayContainLexicalAccessInBranch) {
         MOZ_ASSERT(tdzCache_.isSome());
         tdzCache_.reset();
     }
@@ -133,67 +91,27 @@ IfEmitter::emitElseInternal()
     if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
         return false;
 
     // Clear jumpAroundThen_ offset, to tell emitEnd there was an else part.
     jumpAroundThen_ = JumpList();
 
     // Restore stack depth of the then part.
     bce_->stackDepth = thenDepth_;
-#ifdef DEBUG
-    state_ = State::Else;
-#endif
-    return true;
-}
-
-bool
-IfEmitter::emitElse()
-{
-    MOZ_ASSERT(state_ == State::ThenElse || state_ == State::Cond);
-
-    if (!emitElseInternal())
-        return false;
 
     // Enclose else-branch with TDZCheckCache.
     if (kind_ == Kind::MayContainLexicalAccessInBranch)
         tdzCache_.emplace(bce_);
 
-#ifdef DEBUG
-    state_ = State::Else;
-#endif
     return true;
 }
 
 bool
-IfEmitter::emitElseIf()
+BranchEmitterBase::emitEndInternal()
 {
-    MOZ_ASSERT(state_ == State::ThenElse);
-
-    if (!emitElseInternal())
-        return false;
-
-    // Enclose cond for else-if with TDZCheckCache.
-    if (kind_ == Kind::MayContainLexicalAccessInBranch)
-        tdzCache_.emplace(bce_);
-
-#ifdef DEBUG
-    state_ = State::ElseIf;
-#endif
-    return true;
-}
-
-bool
-IfEmitter::emitEnd()
-{
-    MOZ_ASSERT(state_ == State::Then || state_ == State::Else);
-    // If there was an else part for the last branch, jumpAroundThen_ is
-    // already fixed up when emitting the else part.
-    MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1);
-    MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1);
-
     // The end of TDZCheckCache for then or else-clause.
     if (kind_ == Kind::MayContainLexicalAccessInBranch) {
         MOZ_ASSERT(tdzCache_.isSome());
         tdzCache_.reset();
     }
 
     calculateOrCheckPushed();
 
@@ -203,17 +121,178 @@ IfEmitter::emitEnd()
         if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
             return false;
     }
 
     // Patch all the jumps around else parts.
     if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
         return false;
 
+    return true;
+}
+
+bool
+IfEmitter::emitIf(const Maybe<uint32_t>& ifPos)
+{
+    MOZ_ASSERT(state_ == State::Start);
+
+    if (ifPos) {
+        // Make sure this code is attributed to the "if" so that it gets a
+        // useful column number, instead of the default 0 value.
+        if (!bce_->updateSourceCoordNotes(*ifPos))
+            return false;
+    }
+
+#ifdef DEBUG
+    state_ = State::If;
+#endif
+    return true;
+}
+
+bool
+IfEmitter::emitThen()
+{
+    MOZ_ASSERT(state_ == State::If || state_ == State::ElseIf);
+    MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
+    MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
+
+    if (!emitThenInternal(SRC_IF))
+        return false;
+
+#ifdef DEBUG
+    state_ = State::Then;
+#endif
+    return true;
+}
+
+bool
+IfEmitter::emitThenElse()
+{
+    MOZ_ASSERT(state_ == State::If || state_ == State::ElseIf);
+    MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
+    MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
+
+    if (!emitThenInternal(SRC_IF_ELSE))
+        return false;
+
+#ifdef DEBUG
+    state_ = State::ThenElse;
+#endif
+    return true;
+}
+
+bool
+IfEmitter::emitElseIf(const Maybe<uint32_t>& ifPos)
+{
+    MOZ_ASSERT(state_ == State::ThenElse);
+
+    if (!emitElseInternal())
+        return false;
+
+    if (ifPos) {
+        // Make sure this code is attributed to the "if" so that it gets a
+        // useful column number, instead of the default 0 value.
+        if (!bce_->updateSourceCoordNotes(*ifPos))
+            return false;
+    }
+
+#ifdef DEBUG
+    state_ = State::ElseIf;
+#endif
+    return true;
+}
+
+bool
+IfEmitter::emitElse()
+{
+    MOZ_ASSERT(state_ == State::ThenElse);
+
+    if (!emitElseInternal())
+        return false;
+
+#ifdef DEBUG
+    state_ = State::Else;
+#endif
+    return true;
+}
+
+bool
+IfEmitter::emitEnd()
+{
+    MOZ_ASSERT(state_ == State::Then || state_ == State::Else);
+    // If there was an else part for the last branch, jumpAroundThen_ is
+    // already fixed up when emitting the else part.
+    MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1);
+    MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1);
+
+    if (!emitEndInternal())
+        return false;
+
 #ifdef DEBUG
     state_ = State::End;
 #endif
     return true;
 }
 
 InternalIfEmitter::InternalIfEmitter(BytecodeEmitter* bce)
   : IfEmitter(bce, Kind::NoLexicalAccessInBranch)
+{
+#ifdef DEBUG
+    // Skip emitIf (see the comment above InternalIfEmitter declaration).
+    state_ = State::If;
+#endif
+}
+
+CondEmitter::CondEmitter(BytecodeEmitter* bce)
+  : BranchEmitterBase(bce, Kind::MayContainLexicalAccessInBranch)
 {}
+
+bool
+CondEmitter::emitCond()
+{
+    MOZ_ASSERT(state_ == State::Start);
+#ifdef DEBUG
+    state_ = State::Cond;
+#endif
+    return true;
+}
+
+bool
+CondEmitter::emitThenElse()
+{
+    MOZ_ASSERT(state_ == State::Cond);
+    if (!emitThenInternal(SRC_COND))
+        return false;
+
+#ifdef DEBUG
+    state_ = State::ThenElse;
+#endif
+    return true;
+}
+
+bool
+CondEmitter::emitElse()
+{
+    MOZ_ASSERT(state_ == State::ThenElse);
+
+    if (!emitElseInternal())
+        return false;
+
+#ifdef DEBUG
+    state_ = State::Else;
+#endif
+    return true;
+}
+
+bool
+CondEmitter::emitEnd()
+{
+    MOZ_ASSERT(state_ == State::Else);
+    MOZ_ASSERT(jumpAroundThen_.offset == -1);
+
+    if (!emitEndInternal())
+        return false;
+
+#ifdef DEBUG
+    state_ = State::End;
+#endif
+    return true;
+}
--- a/js/src/frontend/IfEmitter.h
+++ b/js/src/frontend/IfEmitter.h
@@ -16,68 +16,33 @@
 #include "frontend/SourceNotes.h"
 #include "frontend/TDZCheckCache.h"
 
 namespace js {
 namespace frontend {
 
 struct BytecodeEmitter;
 
-// Class for emitting bytecode for blocks like if-then-else.
-//
-// This class can be used to emit single if-then-else block, or cascading
-// else-if blocks.
-//
-// Usage: (check for the return value is omitted for simplicity)
-//
-//   `if (cond) then_block`
-//     IfEmitter ifThen(this);
-//     emit(cond);
-//     ifThen.emitThen();
-//     emit(then_block);
-//     ifThen.emitEnd();
-//
-//   `if (cond) then_block else else_block`
-//     IfEmitter ifThenElse(this);
-//     emit(cond);
-//     ifThenElse.emitThenElse();
-//     emit(then_block);
-//     ifThenElse.emitElse();
-//     emit(else_block);
-//     ifThenElse.emitEnd();
-//
-//   `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
-//     IfEmitter ifThenElse(this);
-//     emit(c1);
-//     ifThenElse.emitThenElse();
-//     emit(b1);
-//     ifThenElse.emitElseIf();
-//     emit(c2);
-//     ifThenElse.emitThenElse();
-//     emit(b2);
-//     ifThenElse.emitElseIf();
-//     emit(c3);
-//     ifThenElse.emitThenElse();
-//     emit(b3);
-//     ifThenElse.emitElse();
-//     emit(b4);
-//     ifThenElse.emitEnd();
-//
-//   `cond ? then_expr : else_expr`
-//     IfEmitter condElse(this);
-//     emit(cond);
-//     condElse.emitCond();
-//     emit(then_block);
-//     condElse.emitElse();
-//     emit(else_block);
-//     condElse.emitEnd();
-//
-class MOZ_STACK_CLASS IfEmitter
+class MOZ_STACK_CLASS BranchEmitterBase
 {
-  public:
+  protected:
+    BytecodeEmitter* bce_;
+
+    // Jump around the then clause, to the beginning of the else clause.
+    JumpList jumpAroundThen_;
+
+    // Jump around the else clause, to the end of the entire branch.
+    JumpList jumpsAroundElse_;
+
+    // The stack depth before emitting the then block.
+    // Used for restoring stack depth before emitting the else block.
+    // Also used for assertion to make sure then and else blocks pushed the
+    // same number of values.
+    int32_t thenDepth_ = 0;
+
     // Whether the then-clause, the else-clause, or else-if condition may
     // contain declaration or access to lexical variables, which means they
     // should have their own TDZCheckCache.  Basically TDZCheckCache should be
     // created for each basic block, which then-clause, else-clause, and
     // else-if condition are, but for internally used branches which are
     // known not to touch lexical variables we can skip creating TDZCheckCache
     // for them.
     //
@@ -87,131 +52,253 @@ class MOZ_STACK_CLASS IfEmitter
         // which basically may contain declaration or accesses to lexical
         // variables inside then-clause, else-clause, and else-if condition.
         MayContainLexicalAccessInBranch,
 
         // For internally used branches which don't touch lexical variables
         // inside then-clause, else-clause, nor else-if condition.
         NoLexicalAccessInBranch
     };
-
-  private:
-    BytecodeEmitter* bce_;
-
-    // Jump around the then clause, to the beginning of the else clause.
-    JumpList jumpAroundThen_;
+    Kind kind_;
 
-    // Jump around the else clause, to the end of the entire branch.
-    JumpList jumpsAroundElse_;
-
-    // The stack depth before emitting the then block.
-    // Used for restoring stack depth before emitting the else block.
-    // Also used for assertion to make sure then and else blocks pushed the
-    // same number of values.
-    int32_t thenDepth_;
-
-    Kind kind_;
     mozilla::Maybe<TDZCheckCache> tdzCache_;
 
 #ifdef DEBUG
     // The number of values pushed in the then and else blocks.
-    int32_t pushed_;
-    bool calculatedPushed_;
-
-    // The state of this emitter.
-    //
-    // +-------+   emitCond +------+ emitElse +------+        emitEnd +-----+
-    // | Start |-+--------->| Cond |--------->| Else |------>+------->| End |
-    // +-------+ |          +------+          +------+       ^        +-----+
-    //           |                                           |
-    //           v emitThen +------+                         |
-    //        +->+--------->| Then |------------------------>+
-    //        ^  |          +------+                         ^
-    //        |  |                                           |
-    //        |  |                                           +---+
-    //        |  |                                               |
-    //        |  | emitThenElse +----------+   emitElse +------+ |
-    //        |  +------------->| ThenElse |-+--------->| Else |-+
-    //        |                 +----------+ |          +------+
-    //        |                              |
-    //        |                              | emitElseIf +--------+
-    //        |                              +----------->| ElseIf |-+
-    //        |                                           +--------+ |
-    //        |                                                      |
-    //        +------------------------------------------------------+
-    enum class State {
-        // The initial state.
-        Start,
-
-        // After calling emitThen.
-        Then,
-
-        // After calling emitCond.
-        Cond,
-
-        // After calling emitThenElse.
-        ThenElse,
-
-        // After calling emitElse.
-        Else,
-
-        // After calling emitElseIf.
-        ElseIf,
-
-        // After calling emitEnd.
-        End
-    };
-    State state_;
+    int32_t pushed_ = 0;
+    bool calculatedPushed_ = false;
 #endif
 
   protected:
-    // For InternalIfEmitter.
-    IfEmitter(BytecodeEmitter* bce, Kind kind);
+    BranchEmitterBase(BytecodeEmitter* bce, Kind kind);
+
+    MOZ_MUST_USE bool emitThenInternal(SrcNoteType type);
+    void calculateOrCheckPushed();
+    MOZ_MUST_USE bool emitElseInternal();
+    MOZ_MUST_USE bool emitEndInternal();
 
   public:
-    explicit IfEmitter(BytecodeEmitter* bce);
-
-    MOZ_MUST_USE bool emitThen();
-    MOZ_MUST_USE bool emitCond();
-    MOZ_MUST_USE bool emitThenElse();
-
-    MOZ_MUST_USE bool emitElse();
-    MOZ_MUST_USE bool emitElseIf();
-
-    MOZ_MUST_USE bool emitEnd();
-
 #ifdef DEBUG
     // Returns the number of values pushed onto the value stack inside
     // `then_block` and `else_block`.
     // Can be used in assertion after emitting if-then-else.
     int32_t pushed() const {
         return pushed_;
     }
 
     // Returns the number of values popped onto the value stack inside
     // `then_block` and `else_block`.
     // Can be used in assertion after emitting if-then-else.
     int32_t popped() const {
         return -pushed_;
     }
 #endif
+};
 
-  private:
-    MOZ_MUST_USE bool emitIfInternal(SrcNoteType type);
-    void calculateOrCheckPushed();
-    MOZ_MUST_USE bool emitElseInternal();
+// Class for emitting bytecode for blocks like if-then-else.
+//
+// This class can be used to emit single if-then-else block, or cascading
+// else-if blocks.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+//   `if (cond) then_block`
+//     IfEmitter ifThen(this);
+//     ifThen.emitIf(Some(offset_of_if));
+//     emit(cond);
+//     ifThen.emitThen();
+//     emit(then_block);
+//     ifThen.emitEnd();
+//
+//   `if (cond) then_block else else_block`
+//     IfEmitter ifThenElse(this);
+//     ifThen.emitIf(Some(offset_of_if));
+//     emit(cond);
+//     ifThenElse.emitThenElse();
+//     emit(then_block);
+//     ifThenElse.emitElse();
+//     emit(else_block);
+//     ifThenElse.emitEnd();
+//
+//   `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
+//     IfEmitter ifThenElse(this);
+//     ifThen.emitIf(Some(offset_of_if));
+//     emit(c1);
+//     ifThenElse.emitThenElse();
+//     emit(b1);
+//     ifThenElse.emitElseIf(Some(offset_of_if));
+//     emit(c2);
+//     ifThenElse.emitThenElse();
+//     emit(b2);
+//     ifThenElse.emitElseIf(Some(offset_of_if));
+//     emit(c3);
+//     ifThenElse.emitThenElse();
+//     emit(b3);
+//     ifThenElse.emitElse();
+//     emit(b4);
+//     ifThenElse.emitEnd();
+//
+class MOZ_STACK_CLASS IfEmitter : public BranchEmitterBase
+{
+  protected:
+#ifdef DEBUG
+    // The state of this emitter.
+    //
+    // +-------+ emitIf +----+
+    // | Start |------->| If |-+
+    // +-------+        +----+ |
+    //                         |
+    //    +--------------------+
+    //    |
+    //    v emitThen +------+                               emitEnd +-----+
+    // +->+--------->| Then |---------------------------->+-------->| End |
+    // ^  |          +------+                             ^         +-----+
+    // |  |                                               |
+    // |  |                                               |
+    // |  |                                               |
+    // |  | emitThenElse +----------+   emitElse +------+ |
+    // |  +------------->| ThenElse |-+--------->| Else |-+
+    // |                 +----------+ |          +------+
+    // |                              |
+    // |                              | emitElseIf +--------+
+    // |                              +----------->| ElseIf |-+
+    // |                                           +--------+ |
+    // |                                                      |
+    // +------------------------------------------------------+
+    enum class State {
+        // The initial state.
+        Start,
+
+        // After calling emitIf.
+        If,
+
+        // After calling emitThen.
+        Then,
+
+        // After calling emitThenElse.
+        ThenElse,
+
+        // After calling emitElse.
+        Else,
+
+        // After calling emitElseIf.
+        ElseIf,
+
+        // After calling emitEnd.
+        End
+    };
+    State state_ = State::Start;
+#endif
+
+  protected:
+    // For InternalIfEmitter.
+    IfEmitter(BytecodeEmitter* bce, Kind kind);
+
+  public:
+    explicit IfEmitter(BytecodeEmitter* bce);
+
+    // `ifPos` is the offset in the source code for the character below:
+    //
+    //   if ( cond ) { ... } else if ( cond2 ) { ... }
+    //   ^                        ^
+    //   |                        |
+    //   |                        ifPos for emitElseIf
+    //   |
+    //   ifPos for emitIf
+    //
+    // Can be Nothing() if not available.
+    MOZ_MUST_USE bool emitIf(const mozilla::Maybe<uint32_t>& ifPos);
+
+    MOZ_MUST_USE bool emitThen();
+    MOZ_MUST_USE bool emitThenElse();
+
+    MOZ_MUST_USE bool emitElseIf(const mozilla::Maybe<uint32_t>& ifPos);
+    MOZ_MUST_USE bool emitElse();
+
+    MOZ_MUST_USE bool emitEnd();
 };
 
 // Class for emitting bytecode for blocks like if-then-else which doesn't touch
 // lexical variables.
 //
 // See the comments above NoLexicalAccessInBranch for more details when to use
 // this instead of IfEmitter.
+// Compared to IfEmitter, this class doesn't have emitIf method, given that
+// it doesn't have syntactic `if`, and also the `cond` value can be already
+// on the stack.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+//   `if (cond) then_block else else_block` (effectively)
+//     emit(cond);
+//     InternalIfEmitter ifThenElse(this);
+//     ifThenElse.emitThenElse();
+//     emit(then_block);
+//     ifThenElse.emitElse();
+//     emit(else_block);
+//     ifThenElse.emitEnd();
+//
 class MOZ_STACK_CLASS InternalIfEmitter : public IfEmitter
 {
   public:
     explicit InternalIfEmitter(BytecodeEmitter* bce);
 };
 
+// Class for emitting bytecode for conditional expression.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+//   `cond ? then_expr : else_expr`
+//     CondEmitter condElse(this);
+//     condElse.emitCond();
+//     emit(cond);
+//     condElse.emitThenElse();
+//     emit(then_expr);
+//     condElse.emitElse();
+//     emit(else_expr);
+//     condElse.emitEnd();
+//
+class MOZ_STACK_CLASS CondEmitter : public BranchEmitterBase
+{
+#ifdef DEBUG
+    // The state of this emitter.
+    //
+    // +-------+ emitCond +------+ emitThenElse +----------+
+    // | Start |--------->| Cond |------------->| ThenElse |-+
+    // +-------+          +------+              +----------+ |
+    //                                                       |
+    //                                     +-----------------+
+    //                                     |
+    //                                     | emitElse +------+ emitEnd +-----+
+    //                                     +--------->| Else |-------->| End |
+    //                                                +------+         +-----+
+    enum class State {
+        // The initial state.
+        Start,
+
+        // After calling emitCond.
+        Cond,
+
+        // After calling emitThenElse.
+        ThenElse,
+
+        // After calling emitElse.
+        Else,
+
+        // After calling emitEnd.
+        End
+    };
+    State state_ = State::Start;
+#endif
+
+  public:
+    explicit CondEmitter(BytecodeEmitter* bce);
+
+    MOZ_MUST_USE bool emitCond();
+    MOZ_MUST_USE bool emitThenElse();
+    MOZ_MUST_USE bool emitElse();
+    MOZ_MUST_USE bool emitEnd();
+};
+
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_IfEmitter_h */
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -70,21 +70,21 @@ class NameResolver
      * Walk over the given ParseNode, attempting to convert it to a stringified
      * name that respresents where the function is being assigned to.
      *
      * |*foundName| is set to true if a name is found for the expression.
      */
     bool nameExpression(ParseNode* n, bool* foundName) {
         switch (n->getKind()) {
           case ParseNodeKind::Dot:
-            if (!nameExpression(n->expr(), foundName))
+            if (!nameExpression(n->pn_left, foundName))
                 return false;
             if (!*foundName)
                 return true;
-            return appendPropertyReference(n->pn_atom);
+            return appendPropertyReference(n->pn_right->pn_atom);
 
           case ParseNodeKind::Name:
             *foundName = true;
             return buf->append(n->pn_atom);
 
           case ParseNodeKind::This:
             *foundName = true;
             return buf->append("this");
@@ -310,27 +310,27 @@ class NameResolver
 
             element = element->pn_next;
         }
     }
 
     bool resolveTaggedTemplate(ParseNode* node, HandleAtom prefix) {
         MOZ_ASSERT(node->isKind(ParseNodeKind::TaggedTemplate));
 
-        ParseNode* element = node->pn_head;
+        ParseNode* tag = node->pn_left;
 
-        // The list head is a leading expression, e.g. |tag| in |tag`foo`|,
+        // The leading expression, e.g. |tag| in |tag`foo`|,
         // that might contain functions.
-        if (!resolve(element, prefix))
+        if (!resolve(tag, prefix))
             return false;
 
-        // Next is the callsite object node.  This node only contains
+        // The callsite object node is first.  This node only contains
         // internal strings or undefined and an array -- no user-controlled
         // expressions.
-        element = element->pn_next;
+        ParseNode* element = node->pn_right->pn_head;
 #ifdef DEBUG
         {
             MOZ_ASSERT(element->isKind(ParseNodeKind::CallSiteObj));
             ParseNode* array = element->pn_head;
             MOZ_ASSERT(array->isKind(ParseNodeKind::Array));
             for (ParseNode* kid = array->pn_head; kid; kid = kid->pn_next)
                 MOZ_ASSERT(kid->isKind(ParseNodeKind::TemplateString));
             for (ParseNode* next = array->pn_next; next; next = next->pn_next) {
@@ -692,19 +692,16 @@ class NameResolver
           case ParseNodeKind::Add:
           case ParseNodeKind::Sub:
           case ParseNodeKind::Star:
           case ParseNodeKind::Div:
           case ParseNodeKind::Mod:
           case ParseNodeKind::Pow:
           case ParseNodeKind::Pipeline:
           case ParseNodeKind::Comma:
-          case ParseNodeKind::New:
-          case ParseNodeKind::Call:
-          case ParseNodeKind::SuperCall:
           case ParseNodeKind::Array:
           case ParseNodeKind::StatementList:
           case ParseNodeKind::ParamsBody:
           // Initializers for individual variables, and computed property names
           // within destructuring patterns, may contain unnamed functions.
           case ParseNodeKind::Var:
           case ParseNodeKind::Const:
           case ParseNodeKind::Let:
@@ -728,21 +725,42 @@ class NameResolver
           // contents with expressions interpolated into the overall literal.
           case ParseNodeKind::TemplateStringList:
             MOZ_ASSERT(cur->isArity(PN_LIST));
             if (!resolveTemplateLiteral(cur, prefix))
                 return false;
             break;
 
           case ParseNodeKind::TaggedTemplate:
-            MOZ_ASSERT(cur->isArity(PN_LIST));
+            MOZ_ASSERT(cur->isArity(PN_BINARY));
             if (!resolveTaggedTemplate(cur, prefix))
                 return false;
             break;
 
+          case ParseNodeKind::New:
+          case ParseNodeKind::Call:
+          case ParseNodeKind::SuperCall:
+            MOZ_ASSERT(cur->isArity(PN_BINARY));
+            if (!resolve(cur->pn_left, prefix))
+                return false;
+            if (!resolve(cur->pn_right, prefix))
+                return false;
+            break;
+
+          // Handles the arguments for new/call/supercall, but does _not_ handle
+          // the Arguments node used by tagged template literals, since that is
+          // special-cased inside of resolveTaggedTemplate.
+          case ParseNodeKind::Arguments:
+            MOZ_ASSERT(cur->isArity(PN_LIST));
+            for (ParseNode* element = cur->pn_head; element; element = element->pn_next) {
+                if (!resolve(element, prefix))
+                    return false;
+            }
+            break;
+
           // Import/export spec lists contain import/export specs containing
           // only pairs of names. Alternatively, an export spec lists may
           // contain a single export batch specifier.
           case ParseNodeKind::ExportSpecList:
           case ParseNodeKind::ImportSpecList: {
             MOZ_ASSERT(cur->isArity(PN_LIST));
 #ifdef DEBUG
             bool isImport = cur->isKind(ParseNodeKind::ImportSpecList);
@@ -761,22 +779,22 @@ class NameResolver
                 MOZ_ASSERT(item->pn_right->isKind(ParseNodeKind::Name));
                 MOZ_ASSERT(!item->pn_right->expr());
             }
 #endif
             break;
           }
 
           case ParseNodeKind::Dot:
-            MOZ_ASSERT(cur->isArity(PN_NAME));
+            MOZ_ASSERT(cur->isArity(PN_BINARY));
 
             // Super prop nodes do not have a meaningful LHS
             if (cur->as<PropertyAccess>().isSuper())
                 break;
-            if (!resolve(cur->expr(), prefix))
+            if (!resolve(cur->pn_left, prefix))
                 return false;
             break;
 
           case ParseNodeKind::Label:
             MOZ_ASSERT(cur->isArity(PN_NAME));
             if (!resolve(cur->expr(), prefix))
                 return false;
             break;
@@ -805,16 +823,17 @@ class NameResolver
             break;
 
           // Kinds that should be handled by parent node resolution.
 
           case ParseNodeKind::ImportSpec: // by ParseNodeKind::ImportSpecList
           case ParseNodeKind::ExportSpec: // by ParseNodeKind::ExportSpecList
           case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate
           case ParseNodeKind::ClassNames:  // by ParseNodeKind::Class
+          case ParseNodeKind::PropertyName:  // by ParseNodeKind::Dot
             MOZ_CRASH("should have been handled by a parent node");
 
           case ParseNodeKind::Limit: // invalid sentinel value
             MOZ_CRASH("invalid node kind");
         }
 
         nparents--;
         MOZ_ASSERT(initialParents == nparents, "nparents imbalance detected");
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -211,16 +211,31 @@ UnaryNode::dump(GenericPrinter& out, int
     indent += strlen(name) + 2;
     DumpParseTree(pn_kid, out, indent);
     out.printf(")");
 }
 
 void
 BinaryNode::dump(GenericPrinter& out, int indent)
 {
+    if (isKind(ParseNodeKind::Dot)) {
+        out.put("(.");
+
+        DumpParseTree(pn_right, out, indent + 2);
+
+        out.putChar(' ');
+        if (as<PropertyAccess>().isSuper())
+            out.put("super");
+        else
+            DumpParseTree(pn_left, out, indent + 2);
+
+        out.printf(")");
+        return;
+    }
+
     const char* name = parseNodeNames[size_t(getKind())];
     out.printf("(%s ", name);
     indent += strlen(name) + 2;
     DumpParseTree(pn_left, out, indent);
     IndentNewLine(out, indent);
     DumpParseTree(pn_right, out, indent);
     out.printf(")");
 }
@@ -283,43 +298,31 @@ DumpName(GenericPrinter& out, const Char
         else
             out.printf("\\u%04x", unsigned(c));
     }
 }
 
 void
 NameNode::dump(GenericPrinter& out, int indent)
 {
-    if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::Dot)) {
-        if (isKind(ParseNodeKind::Dot))
-            out.put("(.");
-
+    if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::PropertyName)) {
         if (!pn_atom) {
             out.put("#<null name>");
         } else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) {
             // Dump destructuring parameter.
             out.put("(#<zero-length name> ");
             DumpParseTree(expr(), out, indent + 21);
             out.printf(")");
         } else {
             JS::AutoCheckCannotGC nogc;
             if (pn_atom->hasLatin1Chars())
                 DumpName(out, pn_atom->latin1Chars(nogc), pn_atom->length());
             else
                 DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length());
         }
-
-        if (isKind(ParseNodeKind::Dot)) {
-            out.putChar(' ');
-            if (as<PropertyAccess>().isSuper())
-                out.put("super");
-            else
-                DumpParseTree(expr(), out, indent + 2);
-            out.printf(")");
-        }
         return;
     }
 
     const char* name = parseNodeNames[size_t(getKind())];
     out.printf("(%s ", name);
     indent += strlen(name) + 2;
     DumpParseTree(expr(), out, indent);
     out.printf(")");
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -50,24 +50,26 @@ class ObjectBox;
     F(Colon) \
     F(Shorthand) \
     F(Pos) \
     F(Neg) \
     F(PreIncrement) \
     F(PostIncrement) \
     F(PreDecrement) \
     F(PostDecrement) \
+    F(PropertyName) \
     F(Dot) \
     F(Elem) \
     F(Array) \
     F(Elision) \
     F(StatementList) \
     F(Label) \
     F(Object) \
     F(Call) \
+    F(Arguments) \
     F(Name) \
     F(ObjectPropertyName) \
     F(ComputedName) \
     F(Number) \
     F(String) \
     F(TemplateStringList) \
     F(TemplateString) \
     F(TaggedTemplate) \
@@ -366,34 +368,35 @@ IsTypeofKind(ParseNodeKind kind)
  * Not,
  * BitNot
  * TypeOfName, unary    pn_kid: UNARY expr
  * TypeOfExpr
  * PreIncrement, unary  pn_kid: MEMBER expr
  * PostIncrement,
  * PreDecrement,
  * PostDecrement
- * New      list        pn_head: list of ctor, arg1, arg2, ... argN
- *                          pn_count: 1 + N (where N is number of args)
- *                          ctor is a MEMBER expr
+ * New      binary      pn_left: ctor expression on the left of the (
+ *                          pn_right: Arguments
  * DeleteName unary     pn_kid: Name expr
  * DeleteProp unary     pn_kid: Dot expr
  * DeleteElem unary     pn_kid: Elem expr
  * DeleteExpr unary     pn_kid: MEMBER expr that's evaluated, then the
  *                          overall delete evaluates to true; can't be a kind
  *                          for a more-specific PNK_DELETE* unless constant
  *                          folding (or a similar parse tree manipulation) has
  *                          occurred
- * Dot      name        pn_expr: MEMBER expr to left of .
- *                          pn_atom: name to right of .
+ * PropertyName name    pn_atom: property name being accessed
+ * Dot      binary      pn_left: MEMBER expr to left of .
+ *                          pn_right: PropertyName to right of .
  * Elem     binary      pn_left: MEMBER expr to left of [
  *                          pn_right: expr between [ and ]
- * Call     list        pn_head: list of call, arg1, arg2, ... argN
- *                          pn_count: 1 + N (where N is number of args)
- *                          call is a MEMBER expr naming a callable object
+ * Call     binary      pn_left: callee expression on the left of the (
+ *                          pn_right: Arguments
+ * Arguments list       pn_head: list of arg1, arg2, ... argN
+ *                          pn_count: N (where N is number of args)
  * Array    list        pn_head: list of pn_count array element exprs
  *                          [,,] holes are represented by Elision nodes
  *                          pn_xflags: PN_ENDCOMMA if extra comma at end
  * Object   list        pn_head: list of pn_count binary Colon nodes
  * Colon    binary      key-value pair in object initializer or
  *                          destructuring lhs
  *                          pn_left: property id, pn_right: value
  * Shorthand binary     Same fields as Colon. This is used for object
@@ -403,30 +406,31 @@ IsTypeofKind(ParseNodeKind kind)
  * Name,    name        pn_atom: name, string, or object atom
  * String               pn_op: JSOP_GETNAME, JSOP_STRING, or JSOP_OBJECT
  *                          If JSOP_GETNAME, pn_op may be JSOP_*ARG or JSOP_*VAR
  *                          telling const-ness and static analysis results
  * TemplateStringList pn_head: list of alternating expr and template strings
  *              list
  * TemplateString      pn_atom: template string atom
                 nullary     pn_op: JSOP_NOP
- * TaggedTemplate      pn_head: list of call, call site object, arg1, arg2, ... argN
- *              list        pn_count: 2 + N (N is the number of substitutions)
+ * TaggedTemplate      pn_left: tag expression
+ *              binary       pn_right: Arguments, with the first being the
+ *                           call site object, then arg1, arg2, ... argN
  * CallSiteObj list     pn_head: a Array node followed by
  *                          list of pn_count - 1 TemplateString nodes
  * RegExp   nullary     pn_objbox: RegExp model object
  * Number   dval        pn_dval: double value of numeric literal
  * True,    nullary     pn_op: JSOp bytecode
  * False,
  * Null,
  * RawUndefined
  *
  * This,        unary   pn_kid: '.this' Name if function `this`, else nullptr
  * SuperBase    unary   pn_kid: '.this' Name
- *
+ * SuperCall    binary  pn_left: SuperBase pn_right: Arguments
  * SetThis      binary  pn_left: '.this' Name, pn_right: SuperCall
  *
  * LexicalScope scope   pn_u.scope.bindings: scope bindings
  *                          pn_u.scope.body: scope body
  * Generator    nullary
  * InitialYield unary   pn_kid: generator object
  * Yield,       unary   pn_kid: expr or null
  * YieldStar,
@@ -566,18 +570,17 @@ class ParseNode
         } unary;
         struct {                        /* name, labeled statement, etc. */
             union {
                 JSAtom*      atom;      /* lexical name or label atom */
                 ObjectBox*   objbox;    /* regexp object */
                 FunctionBox* funbox;    /* function object */
             };
             ParseNode*  expr;           /* module or function body, var
-                                           initializer, argument default, or
-                                           base object of ParseNodeKind::Dot */
+                                           initializer, or argument default */
         } name;
         struct {
             LexicalScope::Data* bindings;
             ParseNode*          body;
         } scope;
         struct {
             double       value;         /* aligned numeric literal value */
             DecimalPoint decimalPoint;  /* Whether the number has a decimal point */
@@ -1171,40 +1174,43 @@ class RegExpLiteral : public NullaryNode
     static bool test(const ParseNode& node) {
         bool match = node.isKind(ParseNodeKind::RegExp);
         MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
         MOZ_ASSERT_IF(match, node.isOp(JSOP_REGEXP));
         return match;
     }
 };
 
-class PropertyAccess : public ParseNode
+class PropertyAccess : public BinaryNode
 {
   public:
-    PropertyAccess(ParseNode* lhs, PropertyName* name, uint32_t begin, uint32_t end)
-      : ParseNode(ParseNodeKind::Dot, JSOP_NOP, PN_NAME, TokenPos(begin, end))
+    /*
+     * PropertyAccess nodes can have any expression/'super' as left-hand
+     * side, but the name must be a ParseNodeKind::PropertyName node.
+     */
+    PropertyAccess(ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end)
+      : BinaryNode(ParseNodeKind::Dot, JSOP_NOP, TokenPos(begin, end), lhs, name)
     {
         MOZ_ASSERT(lhs != nullptr);
         MOZ_ASSERT(name != nullptr);
-        pn_u.name.expr = lhs;
-        pn_u.name.atom = name;
     }
 
     static bool test(const ParseNode& node) {
         bool match = node.isKind(ParseNodeKind::Dot);
-        MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
+        MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
+        MOZ_ASSERT_IF(match, node.pn_right->isKind(ParseNodeKind::PropertyName));
         return match;
     }
 
     ParseNode& expression() const {
-        return *pn_u.name.expr;
+        return *pn_u.binary.left;
     }
 
     PropertyName& name() const {
-        return *pn_u.name.atom->asPropertyName();
+        return *pn_u.binary.right->pn_atom->asPropertyName();
     }
 
     bool isSuper() const {
         // ParseNodeKind::SuperBase cannot result from any expression syntax.
         return expression().isKind(ParseNodeKind::SuperBase);
     }
 };
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3392,34 +3392,34 @@ GeneralParser<ParseHandler, CharT>::addE
         return false;
     }
 
     return tokenStream.getToken(ttp, TokenStream::TemplateTail);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList,
+GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node tagArgsList,
                                                    TokenKind tt)
 {
     Node callSiteObjNode = handler.newCallSiteObject(pos().begin);
     if (!callSiteObjNode)
         return false;
-    handler.addList(nodeList, callSiteObjNode);
+    handler.addList(tagArgsList, callSiteObjNode);
 
     while (true) {
         if (!appendToCallSiteObj(callSiteObjNode))
             return false;
         if (tt != TokenKind::TemplateHead)
             break;
 
-        if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
+        if (!addExprAndGetNextTemplStrToken(yieldHandling, tagArgsList, &tt))
             return false;
     }
-    handler.setEndPosition(nodeList, callSiteObjNode);
+    handler.setEndPosition(tagArgsList, callSiteObjNode);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling)
 {
     Node pn = noSubstitutionUntaggedTemplate();
@@ -8638,68 +8638,71 @@ GeneralParser<ParseHandler, CharT>::assi
             errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
             return null();
         }
     }
     return res;
 }
 
 template <class ParseHandler, typename CharT>
-bool
-GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode,
-                                                 bool* isSpread,
+typename ParseHandler::Node
+GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, bool* isSpread,
                                                  PossibleError* possibleError /* = nullptr */)
 {
+    Node argsList = handler.newArguments(pos());
+    if (!argsList)
+        return null();
+
     bool matched;
     if (!tokenStream.matchToken(&matched, TokenKind::Rp, TokenStream::Operand))
-        return false;
+        return null();
     if (matched) {
-        handler.setEndPosition(listNode, pos().end);
-        return true;
+        handler.setEndPosition(argsList, pos().end);
+        return argsList;
     }
 
     while (true) {
         bool spread = false;
         uint32_t begin = 0;
         if (!tokenStream.matchToken(&matched, TokenKind::TripleDot, TokenStream::Operand))
-            return false;
+            return null();
         if (matched) {
             spread = true;
             begin = pos().begin;
             *isSpread = true;
         }
 
         Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError);
         if (!argNode)
-            return false;
+            return null();
         if (spread) {
             argNode = handler.newSpread(begin, argNode);
             if (!argNode)
-                return false;
-        }
-
-        handler.addList(listNode, argNode);
+                return null();
+        }
+
+        handler.addList(argsList, argNode);
 
         bool matched;
         if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::Operand))
-            return false;
+            return null();
         if (!matched)
             break;
 
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
         if (tt == TokenKind::Rp)
             break;
     }
 
     MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
 
-    handler.setEndPosition(listNode, pos().end);
-    return true;
+    handler.setEndPosition(argsList, pos().end);
+    return argsList;
 }
 
 bool
 ParserBase::checkAndMarkSuperScope()
 {
     if (!pc->sc()->allowSuperProperty())
         return false;
 
@@ -8735,30 +8738,37 @@ GeneralParser<ParseHandler, CharT>::memb
             // Gotten by tryNewTarget
             tt = anyChars.currentToken().type;
             Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt,
                                        /* allowCallSyntax = */ false,
                                        /* possibleError = */ nullptr, PredictInvoked);
             if (!ctorExpr)
                 return null();
 
-            lhs = handler.newNewExpression(newBegin, ctorExpr);
-            if (!lhs)
-                return null();
-
             bool matched;
             if (!tokenStream.matchToken(&matched, TokenKind::Lp))
                 return null();
+
+            bool isSpread = false;
+            Node args;
             if (matched) {
-                bool isSpread = false;
-                if (!argumentList(yieldHandling, lhs, &isSpread))
-                    return null();
-                if (isSpread)
-                    handler.setOp(lhs, JSOP_SPREADNEW);
+                args = argumentList(yieldHandling, &isSpread);
+            } else {
+                args = handler.newArguments(pos());
             }
+
+            if (!args)
+                return null();
+
+            lhs = handler.newNewExpression(newBegin, ctorExpr, args);
+            if (!lhs)
+                return null();
+
+            if (isSpread)
+                handler.setOp(lhs, JSOP_SPREADNEW);
         }
     } else if (tt == TokenKind::Super) {
         Node thisName = newThisName();
         if (!thisName)
             return null();
         lhs = handler.newSuperBase(thisName, pos());
         if (!lhs)
             return null();
@@ -8785,17 +8795,22 @@ GeneralParser<ParseHandler, CharT>::memb
             if (!tokenStream.getToken(&tt))
                 return null();
             if (TokenKindIsPossibleIdentifierName(tt)) {
                 PropertyName* field = anyChars.currentName();
                 if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
                     error(JSMSG_BAD_SUPERPROP, "property");
                     return null();
                 }
-                nextMember = handler.newPropertyAccess(lhs, field, pos().end);
+
+                Node name = handler.newPropertyName(field, pos());
+                if (!name)
+                    return null();
+
+                nextMember = handler.newPropertyAccess(lhs, name);
                 if (!nextMember)
                     return null();
             } else {
                 error(JSMSG_NAME_AFTER_DOT);
                 return null();
             }
         } else if (tt == TokenKind::Lb) {
             Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
@@ -8821,25 +8836,26 @@ GeneralParser<ParseHandler, CharT>::memb
                     return null();
                 }
 
                 if (tt != TokenKind::Lp) {
                     error(JSMSG_BAD_SUPER);
                     return null();
                 }
 
-                nextMember = handler.newSuperCall(lhs);
-                if (!nextMember)
-                    return null();
-
                 // Despite the fact that it's impossible to have |super()| in a
                 // generator, we still inherit the yieldHandling of the
                 // memberExpression, per spec. Curious.
                 bool isSpread = false;
-                if (!argumentList(yieldHandling, nextMember, &isSpread))
+                Node args = argumentList(yieldHandling, &isSpread);
+                if (!args)
+                    return null();
+
+                nextMember = handler.newSuperCall(lhs, args);
+                if (!nextMember)
                     return null();
 
                 if (isSpread)
                     handler.setOp(nextMember, JSOP_SPREADSUPERCALL);
 
                 Node thisName = newThisName();
                 if (!thisName)
                     return null();
@@ -8848,23 +8864,16 @@ GeneralParser<ParseHandler, CharT>::memb
                 if (!nextMember)
                     return null();
             } else {
                 if (options().selfHostingMode && handler.isPropertyAccess(lhs)) {
                     error(JSMSG_SELFHOSTED_METHOD_CALL);
                     return null();
                 }
 
-                TokenPos nextMemberPos = pos();
-                nextMember = tt == TokenKind::Lp
-                             ? handler.newCall(nextMemberPos)
-                             : handler.newTaggedTemplate(nextMemberPos);
-                if (!nextMember)
-                    return null();
-
                 JSOp op = JSOP_CALL;
                 bool maybeAsyncArrow = false;
                 if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
                     // Use the JSOP_FUN{APPLY,CALL} optimizations given the
                     // right syntax.
                     if (prop == context->names().apply) {
                         op = JSOP_FUNAPPLY;
                         if (pc->isFunctionBox())
@@ -8896,34 +8905,44 @@ GeneralParser<ParseHandler, CharT>::memb
                         // If we're in a method, mark the method as requiring
                         // support for 'super', since direct eval code can use
                         // it. (If we're not in a method, that's fine, so
                         // ignore the return value.)
                         checkAndMarkSuperScope();
                     }
                 }
 
-                handler.setBeginPosition(nextMember, lhs);
-                handler.addList(nextMember, lhs);
-
                 if (tt == TokenKind::Lp) {
                     bool isSpread = false;
                     PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr;
-                    if (!argumentList(yieldHandling, nextMember, &isSpread, asyncPossibleError))
+                    Node args = argumentList(yieldHandling, &isSpread, asyncPossibleError);
+                    if (!args)
                         return null();
                     if (isSpread) {
                         if (op == JSOP_EVAL)
                             op = JSOP_SPREADEVAL;
                         else if (op == JSOP_STRICTEVAL)
                             op = JSOP_STRICTSPREADEVAL;
                         else
                             op = JSOP_SPREADCALL;
                     }
+
+                    nextMember = handler.newCall(lhs, args);
+                    if (!nextMember)
+                        return null();
                 } else {
-                    if (!taggedTemplate(yieldHandling, nextMember, tt))
+                    Node args = handler.newArguments(pos());
+                    if (!args)
+                        return null();
+
+                    if (!taggedTemplate(yieldHandling, args, tt))
+                        return null();
+
+                    nextMember = handler.newTaggedTemplate(lhs, args);
+                    if (!nextMember)
                         return null();
                 }
                 handler.setOp(nextMember, op);
             }
         } else {
             anyChars.ungetToken();
             if (handler.isSuperBase(lhs))
                 break;
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -564,18 +564,22 @@ class MOZ_STACK_CLASS PerHandlerParser
     // If ParseHandler is FullParseHandler:
     //   Do nothing.
     inline void clearAbortedSyntaxParse();
 
   public:
     bool isValidSimpleAssignmentTarget(Node node,
                                        FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
 
-    Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) {
-        return handler.newPropertyAccess(expr, key, end);
+    Node newPropertyName(PropertyName* key, const TokenPos& pos) {
+        return handler.newPropertyName(key, pos);
+    }
+
+    Node newPropertyAccess(Node expr, Node key) {
+        return handler.newPropertyAccess(expr, key);
     }
 
     FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
                                 Directives directives, GeneratorKind generatorKind,
                                 FunctionAsyncKind asyncKind);
 };
 
 #define ABORTED_SYNTAX_PARSE_SENTINEL reinterpret_cast<void*>(0x1)
@@ -1150,17 +1154,17 @@ class MOZ_STACK_CLASS GeneralParser
     enum FunctionBodyType { StatementListBody, ExpressionBody };
     Node functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
                       FunctionBodyType type);
 
     Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin);
 
     Node condition(InHandling inHandling, YieldHandling yieldHandling);
 
-    bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread,
+    Node argumentList(YieldHandling yieldHandling, bool* isSpread,
                       PossibleError* possibleError = nullptr);
     Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
                                   TokenKind tt);
     Node destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, YieldHandling yieldHandling,
                                                      TokenKind tt);
 
     inline bool checkExportedName(JSAtom* exportName);
     inline bool checkExportedNamesForArrayBinding(Node node);
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -243,19 +243,21 @@ class SyntaxParseHandler
 
     // Expressions
 
     Node newArrayLiteral(uint32_t begin) { return NodeUnparenthesizedArray; }
     MOZ_MUST_USE bool addElision(Node literal, const TokenPos& pos) { return true; }
     MOZ_MUST_USE bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
     void addArrayElement(Node literal, Node element) { }
 
-    Node newCall(const TokenPos& pos) { return NodeFunctionCall; }
-    Node newSuperCall(Node callee) { return NodeGeneric; }
-    Node newTaggedTemplate(const TokenPos& pos) { return NodeGeneric; }
+    Node newArguments(const TokenPos& pos) { return NodeGeneric; }
+    Node newCall(Node callee, Node args) { return NodeFunctionCall; }
+
+    Node newSuperCall(Node callee, Node args) { return NodeGeneric; }
+    Node newTaggedTemplate(Node callee, Node args) { return NodeGeneric; }
 
     Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
     Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
     Node newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; }
     Node newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
 
     Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
     Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
@@ -327,18 +329,22 @@ class SyntaxParseHandler
     }
 
     Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
     Node newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
         return NodeGeneric;
     }
     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
 
-    Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) {
-        lastAtom = key;
+    Node newPropertyName(PropertyName* name, const TokenPos& pos) {
+        lastAtom = name;
+        return NodeGeneric;
+    }
+
+    Node newPropertyAccess(Node expr, Node key) {
         return NodeDottedProperty;
     }
 
     Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; }
 
     MOZ_MUST_USE bool setupCatchScope(Node letBlock, Node catchName, Node catchBody) {
         return true;
     }
@@ -423,17 +429,17 @@ class SyntaxParseHandler
         MOZ_ASSERT(list == NodeGeneric ||
                    list == NodeUnparenthesizedArray ||
                    list == NodeUnparenthesizedObject ||
                    list == NodeVarDeclaration ||
                    list == NodeLexicalDeclaration ||
                    list == NodeFunctionCall);
     }
 
-    Node newNewExpression(uint32_t begin, Node ctor) {
+    Node newNewExpression(uint32_t begin, Node ctor, Node args) {
         return NodeGeneric;
     }
 
     Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs) {
         return kind == ParseNodeKind::Assign ? NodeUnparenthesizedAssignment : NodeGeneric;
     }
 
     bool isUnparenthesizedAssignment(Node node) {
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -2773,19 +2773,31 @@ ForegroundUpdateKinds(AllocKinds kinds)
             result += kind;
     }
     return result;
 }
 
 void
 GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone)
 {
+    // We need to update each type descriptor object and any objects stored in
+    // its slots, since some of these contain array objects which also need to
+    // be updated.
+
     zone->typeDescrObjects().sweep();
-    for (auto r = zone->typeDescrObjects().all(); !r.empty(); r.popFront())
-        UpdateCellPointers(trc, r.front());
+
+    for (auto r = zone->typeDescrObjects().all(); !r.empty(); r.popFront()) {
+        NativeObject* obj = &r.front()->as<NativeObject>();
+        UpdateCellPointers(trc, obj);
+        for (size_t i = 0; i < obj->slotSpan(); i++) {
+            Value value = obj->getSlot(i);
+            if (value.isObject())
+                UpdateCellPointers(trc, &value.toObject());
+        }
+    }
 }
 
 void
 GCRuntime::updateCellPointers(Zone* zone, AllocKinds kinds, size_t bgTaskCount)
 {
     AllocKinds fgKinds = bgTaskCount == 0 ? kinds : ForegroundUpdateKinds(kinds);
     AllocKinds bgKinds = kinds - fgKinds;
 
--- a/js/src/jit-test/lib/assert-offset-columns.js
+++ b/js/src/jit-test/lib/assert-offset-columns.js
@@ -28,17 +28,22 @@ function assertOffsetColumns(code, expec
     global.eval(execCode);
 
     // Allow some tests to append to a log that will show up in expected ordering.
     const hits = global.hits = [];
     const bpts = new Set();
 
     // Set breakpoints everywhere and call the function.
     const dbg = new Debugger;
-    const script = dbg.addDebuggee(global).makeDebuggeeValue(global.f).script;
+    let debuggeeFn = dbg.addDebuggee(global).makeDebuggeeValue(global.f);
+    if (debuggeeFn.isBoundFunction) {
+        debuggeeFn = debuggeeFn.boundTargetFunction;
+    }
+
+    const { script } = debuggeeFn;
     for (const offset of script.getAllColumnOffsets()) {
         assertEq(offset.lineNumber, 1);
         assertEq(offset.columnNumber < execCode.length, true);
         bpts.add(offset.columnNumber);
 
         script.setBreakpoint(offset.offset, {
             hit(frame) {
                 hits.push(offset.columnNumber);
--- a/js/src/jit-test/tests/cacheir/compare.js
+++ b/js/src/jit-test/tests/cacheir/compare.js
@@ -1,16 +1,193 @@
-function warmup(fun, input, expected) {
-    assertEq(input.length, expected.length);
-    for (var i = 0; i < 30; i++) {
-        for (var j = 0; j < input.length; j++) {
-            lhs = input[j][0];
-            rhs = input[j][1];
-            assertEq(fun(lhs,rhs), expected[j]);
+setJitCompilerOption('ion.forceinlineCaches', 1);
+
+function warmup(fun, input_array) {
+    for (var index = 0; index < input_array.length; index++) {
+        input = input_array[index];
+        input_lhs = input[0];
+        input_rhs = input[1];
+        output    = input[2];
+        for (var i = 0; i < 30; i++) {
+            var str = "";
+            var y = fun(input_lhs, input_rhs);
+            if (y != output) {
+                str = "Computed: "+y+ ", expected: "+ output + " ( " + fun + " lhs: "+ input_lhs + " rhs: " + input_rhs +")";
+            }
+            assertEq(str, "");
         }
     }
 }
 
-var strictCompare = function(a,b) { return a === b; }
-warmup(strictCompare, [[1,1], [3,3], [3,strictCompare],[strictCompare, {}], [3.2, 1],
-                       [0, -0]],
-                       [true, true,  false, false, false,
-                       true]);
\ No newline at end of file
+
+// Int32 + Int32
+var Int32Int32Fun_GT1 = (a, b) => { return a > b; }
+warmup(Int32Int32Fun_GT1, [[1,2, false], [1,1, false], [3,4, false], [4294967295, 2, true],
+                           [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
+
+var Int32Int32Fun_GTE1 = (a, b) => { return a >= b; }
+warmup(Int32Int32Fun_GTE1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, true],
+                            [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
+
+var Int32Int32Fun_LT1 = (a, b) => { return a < b; }
+warmup(Int32Int32Fun_LT1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, false],
+                           [NaN, 2, false],[-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
+
+var Int32Int32Fun_LTE1 = (a, b) => { return a <= b; }
+warmup(Int32Int32Fun_LTE1, [[1,2, true], [1,1, true], [3,4, true], [4294967295, 2, false],
+                            [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
+
+var Int32Int32Fun_EQ1 = (a, b) => { return a == b; }
+warmup(Int32Int32Fun_EQ1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, false],
+                            [NaN, 2, false], [-1000, NaN, false], [undefined, null, true],
+                            ['0', 0, true], [new String('0'), 0, true], [10, undefined, false]]);
+
+var Int32Int32Fun_EQ2 = (a, b) => { return a == b; }
+warmup(Int32Int32Fun_EQ2, [[1, NaN, false], [1, undefined, false], [1, null, false]]);
+
+var Int32Int32Fun_EQ3 = (a, b) => { return a == b; }
+warmup(Int32Int32Fun_EQ3, [[{a: 1}, NaN, false], [{a: 1}, undefined, false], [{a: 1}, null, false]]);
+
+var Int32Int32Fun_EQ4 = (a, b) => { return a == b; }
+warmup(Int32Int32Fun_EQ4, [[undefined, undefined, true], [null, null, true], [null, undefined, true], [undefined, null, true]]);
+
+
+var Int32Int32Fun_NEQ1 = (a, b) => { return a != b; }
+warmup(Int32Int32Fun_NEQ1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, true],
+                            [NaN, 2, true], [-1000, NaN, true], [undefined, null, false],
+                            ['0', 0, false], [new String('0'), 0, false], [10, undefined, true]]);
+
+var Int32Int32Fun_NEQ2 = (a, b) => { return a != b; }
+warmup(Int32Int32Fun_NEQ2, [[1, NaN, true], [1, undefined, true], [1, null, true]]);
+
+var Int32Int32Fun_NEQ3 = (a, b) => { return a != b; }
+warmup(Int32Int32Fun_NEQ3, [[{a: 1}, NaN, true], [{a: 1}, undefined, true], [{a: 1}, null, true]]);
+
+var Int32Int32Fun_NEQ4 = (a, b) => { return a != b; }
+warmup(Int32Int32Fun_NEQ4, [[undefined, undefined, false], [null, null, false], [null, undefined, false], [undefined, null, false]]);
+
+var Int32Int32Fun_SEQ1 = (a, b) => { return a === b; }
+warmup(Int32Int32Fun_SEQ1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, false],
+                            [NaN, 2, false], [-1000, NaN, false], [undefined, null, false],
+                            ['0', 0, false], [new String('0'), 0, false], [10, undefined, false]]);
+
+var Int32Int32Fun_SEQ2 = (a, b) => { return a === b; }
+warmup(Int32Int32Fun_SEQ2, [[1, NaN, false], [1, undefined, false], [1, null, false]]);
+
+var Int32Int32Fun_SEQ3 = (a, b) => { return a === b; }
+warmup(Int32Int32Fun_SEQ3, [[{a: 1}, NaN, false], [{a: 1}, undefined, false], [{a: 1}, null, false]]);
+
+var Int32Int32Fun_SEQ4 = (a, b) => { return a === b; }
+warmup(Int32Int32Fun_SEQ4, [[undefined, undefined, true], [null, null, true], [null, undefined, false], [undefined, null, false] ]);
+
+var Int32Int32Fun_SEQ5 = (a, b) => { return a === b; }
+warmup(Int32Int32Fun_SEQ5, [[1, true, false], [1, false, false], [false, 1, false], [true, 0, false], [true, true, true]]);
+
+var Int32Int32Fun_SNEQ1 = (a, b) => { return a !== b; }
+warmup(Int32Int32Fun_SNEQ1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, true],
+                            [NaN, 2, true], [-1000, NaN, true], [undefined, null, true],
+                            ['0', 0, true], [new String('0'), 0, true], [10, undefined, true]]);
+
+var Int32Int32Fun_SNEQ2 = (a, b) => { return a !== b; }
+warmup(Int32Int32Fun_SNEQ2, [[1, NaN, true], [1, undefined, true], [1, null, true]]);
+
+var Int32Int32Fun_SNEQ3 = (a, b) => { return a !== b; }
+warmup(Int32Int32Fun_SNEQ3, [[{a: 1}, NaN, true], [{a: 1}, undefined, true], [{a: 1}, null, true]]);
+
+var Int32Int32Fun_SNEQ4 = (a, b) => { return a !== b; }
+warmup(Int32Int32Fun_SNEQ4, [[undefined, undefined, false], [null, null, false], [null, undefined, true], [undefined, null, true] ]);
+
+var Int32Int32Fun_SNEQ5 = (a, b) => { return a !== b; }
+warmup(Int32Int32Fun_SNEQ5, [[1, true, true], [1, false, true], [false, 1, true], [true, 0, true]]);
+
+
+// Number Number
+var NumberNumberFun_GT1 = (a, b) => { return a > b; }
+warmup(NumberNumberFun_GT1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
+                             [1,1, false], [3,4, false], [4294967295, 2, true],
+                             [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]);
+
+var NumberNumberFun_GTE1 = (a, b) => { return a >= b; }
+warmup(NumberNumberFun_GTE1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
+                             [1,1, true], [3,4, false], [4294967295, 2, true],
+                              [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]);
+
+var NumberNumberFun_LT1 = (a, b) => { return a < b; }
+warmup(NumberNumberFun_LT1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
+                             [1,1, false], [3,4, true], [4294967295, 2, false],
+                             [NaN, 2, false],[-1000, NaN, false], [NaN, NaN, false, [10.2, undefined, false]]]);
+
+var NumberNumberFun_LTE1 = (a, b) => { return a <= b; }
+warmup(NumberNumberFun_LTE1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
+                              [1,1, true], [3,4, true], [4294967295, 2, false],
+                              [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]);
+
+var NumberNumberFun_EQ1 = (a, b) => { return a == b; }
+warmup(NumberNumberFun_EQ1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
+                             [1,1, true], [3,4, false], [4294967295, 2, false],
+                              [NaN, 2, false], [-1000, NaN, false], [undefined, null, true],
+                            ['0', 0, true], [new String('0'), 0, true], [10.2, undefined, false]]);
+
+var NumberNumberFun_NEQ1 = (a, b) => { return a != b; }
+warmup(NumberNumberFun_NEQ1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
+                              [1,1, false], [3,4, true], [4294967295, 2, true],
+                              [NaN, 2, true], [-1000, NaN, true], [undefined, null, false],
+                            ['0', 0, false], [new String('0'), 0, false], [10.2, undefined, true]]);
+
+var NumberNumberFun_SEQ1 = (a, b) => { return a === b; }
+warmup(NumberNumberFun_SEQ1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
+                             [1,1, true], [3,4, false], [4294967295, 2, false],
+                              [NaN, 2, false], [-1000, NaN, false], [undefined, null, false],
+                            ['0', 0, false], [new String('0'), 0, false], [10.2, undefined, false]]);
+
+var NumberNumberFun_SNEQ1 = (a, b) => { return a !== b; }
+warmup(NumberNumberFun_SNEQ1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
+                               [1,1, false], [3,4, true], [4294967295, 2, true],
+                               [NaN, 2, true], [-1000, NaN, true], [undefined, null, true],
+                               ['0', 0, true], [new String('0'), 0, true], [10.2, undefined, true]]);
+
+// Boolean + Int32
+var BooleanFun_GT1 = (a, b) => { return a > b; }
+warmup(BooleanFun_GT1, [[1,2, false], [true, 2, false], [1,1, false], [true,true, false], [3,4, false], ]);
+
+var BooleanFun_GTE1 = (a, b) => { return a >= b; }
+warmup(BooleanFun_GTE1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false]]);
+
+var BooleanFun_LT1 = (a, b) => { return a < b; }
+warmup(BooleanFun_LT1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true]]);
+
+var BooleanFun_LTE1 = (a, b) => { return a <= b; }
+warmup(BooleanFun_LTE1, [[1,2, true], [true, 2, true], [1,1, true], [true,true, true], [3,4, true]]);
+
+var BooleanFun_EQ1 = (a, b) => { return a == b; }
+warmup(BooleanFun_EQ1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false],
+               ['0', 0, true], [new String('0'), 0, true], [10, undefined, false]]);
+
+var BooleanFun_NEQ1 = (a, b) => { return a != b; }
+warmup(BooleanFun_NEQ1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true],
+             ['0', 0, false], [new String('0'), 0, false], [10, undefined, true]]);
+
+var BooleanFun_SEQ1 = (a, b) => { return a === b; }
+warmup(BooleanFun_SEQ1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false]]);
+
+var BooleanFun_SNEQ1 = (a, b) => { return a !== b; }
+warmup(BooleanFun_SNEQ1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true]]);
+
+// Undefined / Null equality.
+var UndefNull_EQ1 = (a, b) => { return a == b; }
+warmup(UndefNull_EQ1, [[undefined, null, true], [undefined, undefined, true], [null, undefined, true],
+                        [null, null, true], [{a: 10}, undefined, false], [{a: 10}, null, false]]);
+
+var UndefNull_NEQ1 = (a, b) => { return a != b; }
+warmup(UndefNull_NEQ1, [[undefined, null, false], [undefined, undefined, false], [null, undefined, false],
+                         [null, null, false],  [{a: 10}, undefined, true], [{a: 10}, null, true]]);
+
+var UndefNull_SEQ1 = (a, b) => { return a === b; }
+warmup(UndefNull_SEQ1, [[undefined, null, false], [undefined, undefined, true], [null, undefined, false],
+                         [null, null, true],  [{a: 10}, undefined, false], [{a: 10}, null, false]]);
+
+var UndefNull_SNEQ1 = (a, b) => { return a !== b; }
+warmup(UndefNull_SNEQ1, [[undefined, null, true], [undefined, undefined, false], [null, undefined, true],
+                          [null, null, false],  [{a: 10}, undefined, true], [{a: 10}, null, true]]);
+
+var typeDifference = function(a,b) { return a === b; }
+warmup(typeDifference, [[1,1, true], [3,3, true], [3, typeDifference, false],[typeDifference, {}, false],
+                        [3.2, 1, false], [0, -0, true]]);
\ No newline at end of file
--- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js
+++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js
@@ -78,8 +78,63 @@ assertOffsetColumns(
     "                ^                 ^            ^         ^",
 );
 
 // getColumnOffsets correctly places the various parts of a DoWhileStatement.
 assertOffsetColumns(
     "function f(n) { do { print(n); } while(false); }",
     "                ^    ^                         ^",
 );
+
+// getColumnOffsets correctly places the part of normal ::Dot node with identifier root.
+assertOffsetColumns(
+    "var args = [];\n" +
+    "var obj = { base: { a(){ return { b(){} }; } } };\n" +
+    "function f(n) { obj.base.a().b(...args); }",
+    "                ^            ^ ^         ^",
+    "0 2 1 3",
+);
+
+// getColumnOffsets correctly places the part of normal ::Dot node with "this" root.
+assertOffsetColumns(
+    "var args = [];\n" +
+    "var obj = { base: { a(){ return { b(){} }; } } };\n" +
+    "var f = function() { this.base.a().b(...args);  }.bind(obj);",
+    "                     ^             ^ ^          ^",
+    "0 2 1 3",
+);
+
+// getColumnOffsets correctly places the part of normal ::Dot node with "super" base.
+assertOffsetColumns(
+    "var args = [];\n" +
+    "var obj = { base: { a(){ return { b(){} }; } } };\n" +
+    "var f = { __proto__: obj, f(n) { super.base.a().b(...args); } }.f;",
+    "                                 ^              ^ ^         ^",
+    "0 2 1 3",
+);
+
+// getColumnOffsets correctly places the part of normal ::Dot node with other base.
+assertOffsetColumns(
+    "var args = [];\n" +
+    "var obj = { base: { a(){ return { b(){} }; } } };\n" +
+    "function f(n) { (0, obj).base.a().b(...args); }",
+    "                 ^  ^         ^   ^ ^         ^",
+    "0 1 2 4 3 5",
+);
+
+// getColumnOffsets correctly places the part of folded ::Elem node.
+assertOffsetColumns(
+    "var args = [];\n" +
+    "var obj = { base: { a(){ return { b(){} }; } } };\n" +
+    // Constant folding makes the static string behave like a dot access.
+    "function f(n) { obj.base['a']()['b'](...args); }",
+    "                ^               ^    ^         ^",
+    "0 2 1 3",
+);
+
+// getColumnOffsets correctly places the part of computed ::Elem node.
+assertOffsetColumns(
+    "var args = [], a = 'a', b = 'b';\n" +
+    "var obj = { base: { a(){ return { b(){} }; } } };\n" +
+    "function f(n) { obj.base[a]()[b](...args); }",
+    "                ^          ^    ^^         ^",
+    "0 1 3 2 4",
+);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1481093.js
@@ -0,0 +1,5 @@
+v = new new TypedObject.StructType({
+    f: TypedObject.Any
+})
+gczeal(14);
+var lfOffThreadGlobal = newGlobal();
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -5010,16 +5010,137 @@ ICBinaryArith_Fallback::Compiler::genera
     masm.pushValue(R0);
     masm.push(ICStubReg);
     pushStubPayload(masm, R0.scratchReg());
 
     return tailCallVM(DoBinaryArithFallbackInfo, masm);
 }
 
 //
+// Compare_Fallback
+//
+static bool
+DoCompareFallback(JSContext* cx, BaselineFrame* frame, ICCompare_Fallback* stub_, HandleValue lhs,
+                  HandleValue rhs, MutableHandleValue ret)
+{
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICCompare_Fallback*> stub(ICStubEngine::Baseline, frame, stub_);
+
+    RootedScript script(cx, frame->script());
+    jsbytecode* pc = stub->icEntry()->pc(script);
+    JSOp op = JSOp(*pc);
+
+    FallbackICSpew(cx, stub, "Compare(%s)", CodeName[op]);
+
+    // Case operations in a CONDSWITCH are performing strict equality.
+    if (op == JSOP_CASE)
+        op = JSOP_STRICTEQ;
+
+    // Don't pass lhs/rhs directly, we need the original values when
+    // generating stubs.
+    RootedValue lhsCopy(cx, lhs);
+    RootedValue rhsCopy(cx, rhs);
+
+    // Perform the compare operation.
+    bool out;
+    switch (op) {
+      case JSOP_LT:
+        if (!LessThan(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_LE:
+        if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_GT:
+        if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_GE:
+        if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_EQ:
+        if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_NE:
+        if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_STRICTEQ:
+        if (!StrictlyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_STRICTNE:
+        if (!StrictlyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unhandled baseline compare op");
+        return false;
+    }
+
+    ret.setBoolean(out);
+
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
+    // Check to see if a new stub should be generated.
+    if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) {
+        // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
+        // But for now we just bail.
+        return true;
+    }
+
+    if (stub->state().canAttachStub()) {
+        CompareIRGenerator gen(cx, script, pc, stub->state().mode(), op, lhs, rhs);
+        bool attached = false;
+        if (gen.tryAttachStub()) {
+            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                        BaselineCacheIRStubKind::Regular,
+                                                        ICStubEngine::Baseline, script, stub, &attached);
+            if (newStub)
+                    JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+            return true;
+        }
+    }
+
+    stub->noteUnoptimizableAccess();
+    return true;
+}
+
+typedef bool (*DoCompareFallbackFn)(JSContext*, BaselineFrame*, ICCompare_Fallback*,
+                                    HandleValue, HandleValue, MutableHandleValue);
+static const VMFunction DoCompareFallbackInfo =
+    FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, "DoCompareFallback", TailCall,
+                                      PopValues(2));
+
+bool
+ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+    MOZ_ASSERT(R0 == JSReturnOperand);
+
+    // Restore the tail call register.
+    EmitRestoreTailCallReg(masm);
+
+    // Ensure stack is fully synced for the expression decompiler.
+    masm.pushValue(R0);
+    masm.pushValue(R1);
+
+    // Push arguments.
+    masm.pushValue(R1);
+    masm.pushValue(R0);
+    masm.push(ICStubReg);
+    pushStubPayload(masm, R0.scratchReg());
+    return tailCallVM(DoCompareFallbackInfo, masm);
+}
+
+//
 // NewArray_Fallback
 //
 
 static bool
 DoNewArray(JSContext* cx, BaselineFrame* frame, ICNewArray_Fallback* stub, uint32_t length,
            MutableHandleValue res)
 {
     FallbackICSpew(cx, stub, "NewArray");
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jit/BaselineInspector.h"
 
+#include "mozilla/Array.h"
 #include "mozilla/DebugOnly.h"
 
 #include "jit/BaselineIC.h"
 #include "jit/CacheIRCompiler.h"
 
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/ObjectGroup-inl.h"
@@ -407,77 +408,183 @@ BaselineInspector::expectedResultType(js
     switch (stub->kind()) {
       case ICStub::CacheIR_Regular:
         return ParseCacheIRStub(stub);
       default:
         return MIRType::None;
     }
 }
 
-// Whether a baseline stub kind is suitable for a double comparison that
-// converts its operands to doubles.
+// Return the MIRtype corresponding to the guard the reader is pointing
+// to, and ensure that afterwards the reader is pointing to the next op
+// (consume operands).
+//
+// An expected parameter is provided to allow GuardType to check we read
+// the guard from the expected operand in debug builds.
 static bool
-CanUseDoubleCompare(ICStub::Kind kind)
+GuardType(CacheIRReader& reader, mozilla::Array<MIRType,2>& guardType)
 {
-    return kind == ICStub::Compare_Double || kind == ICStub::Compare_NumberWithUndefined;
+    CacheOp op = reader.readOp();
+    uint8_t guardOperand = reader.readByte();
+
+    // We only have two entries for guard types.
+    if (guardOperand > 1)
+        return false;
+
+    // Already assigned this guard a type, fail.
+    if (guardType[guardOperand] != MIRType::None)
+        return false;
+
+    switch (op) {
+        // 0 Skip cases
+        case CacheOp::GuardIsString:
+            guardType[guardOperand] = MIRType::String;
+            break;
+        case CacheOp::GuardIsSymbol:
+            guardType[guardOperand] = MIRType::Symbol;
+            break;
+        case CacheOp::GuardIsNumber:
+            guardType[guardOperand] = MIRType::Double;
+            break;
+        case CacheOp::GuardIsUndefined:
+            guardType[guardOperand] = MIRType::Undefined;
+            break;
+        // 1 skip
+        case CacheOp::GuardIsInt32:
+            guardType[guardOperand] = MIRType::Int32;
+            // Skip over result
+            reader.skip();
+            break;
+        case CacheOp::GuardIsBoolean:
+            guardType[guardOperand] = MIRType::Boolean;
+            // Skip over result
+            reader.skip();
+            break;
+        // Unknown op --
+        default:
+            return false;
+    }
+    return true;
 }
 
-// Whether a baseline stub kind is suitable for an int32 comparison that
-// converts its operands to int32.
+// This code works for all Compare ICs where the pattern is
+//
+//  <Guard LHS/RHS>
+//  <Guard RHS/LHS>
+//  <CompareResult>
+//
+// in other cases (like StrictlyDifferentTypes) it will just
+// return CompareUnknown
+static MCompare::CompareType
+ParseCacheIRStubForCompareType(ICCacheIR_Regular* stub)
+{
+    CacheIRReader reader(stub->stubInfo());
+
+    // Two element array to allow parsing the guards
+    // in whichever order they appear.
+    mozilla::Array<MIRType, 2> guards = { MIRType::None, MIRType::None };
+
+    // Parse out two guards
+    if (!GuardType(reader, guards))
+        return MCompare::Compare_Unknown;
+    if (!GuardType(reader, guards))
+        return MCompare::Compare_Unknown;
+
+    // The lhs and rhs ids are asserted in
+    // CompareIRGenerator::tryAttachStub.
+    MIRType lhs_guard = guards[0];
+    MIRType rhs_guard = guards[1];
+
+    if (lhs_guard == rhs_guard)
+    {
+        if (lhs_guard == MIRType::Int32)
+            return MCompare::Compare_Int32;
+        if (lhs_guard == MIRType::Double)
+            return MCompare::Compare_Double;
+        return MCompare::Compare_Unknown;
+    }
+
+    if ((lhs_guard == MIRType::Int32 && rhs_guard == MIRType::Boolean) ||
+        (lhs_guard == MIRType::Boolean && rhs_guard == MIRType::Int32))
+    {
+        // RHS is converting
+        if (rhs_guard == MIRType::Boolean)
+            return MCompare::Compare_Int32MaybeCoerceRHS;
+
+        return MCompare::Compare_Int32MaybeCoerceLHS;
+    }
+
+    if ((lhs_guard == MIRType::Double && rhs_guard == MIRType::Undefined) ||
+        (lhs_guard == MIRType::Undefined && rhs_guard == MIRType::Double))
+    {
+        // RHS is converting
+        if (rhs_guard == MIRType::Undefined)
+            return MCompare::Compare_DoubleMaybeCoerceRHS;
+
+        return MCompare::Compare_DoubleMaybeCoerceLHS;
+    }
+
+    return MCompare::Compare_Unknown;
+}
+
 static bool
-CanUseInt32Compare(ICStub::Kind kind)
+CoercingCompare(MCompare::CompareType type)
 {
-    return kind == ICStub::Compare_Int32 || kind == ICStub::Compare_Int32WithBoolean;
+    //Prefer the coercing types if they exist, otherwise just use first's type.
+    if (type == MCompare::Compare_DoubleMaybeCoerceLHS ||
+        type == MCompare::Compare_DoubleMaybeCoerceRHS ||
+        type == MCompare::Compare_Int32MaybeCoerceLHS  ||
+        type == MCompare::Compare_Int32MaybeCoerceRHS)
+        return true;
+    return false;
+}
+
+static MCompare::CompareType
+CompatibleType(MCompare::CompareType first, MCompare::CompareType second)
+{
+    // Caller should have dealt with this case.
+    MOZ_ASSERT(first != second);
+
+    //Prefer the coercing types if they exist, otherwise just use first's type.
+    if (CoercingCompare(first))
+        return first;
+
+    if (CoercingCompare(second))
+        return second;
+
+    return first;
 }
 
 MCompare::CompareType
 BaselineInspector::expectedCompareType(jsbytecode* pc)
 {
     ICStub* first = monomorphicStub(pc);
     ICStub* second = nullptr;
     if (!first && !dimorphicStub(pc, &first, &second))
         return MCompare::Compare_Unknown;
 
     if (ICStub* fallback = second ? second->next() : first->next()) {
         MOZ_ASSERT(fallback->isFallback());
         if (fallback->toCompare_Fallback()->hadUnoptimizableAccess())
             return MCompare::Compare_Unknown;
     }
 
-    if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind()))) {
-        ICCompare_Int32WithBoolean* coerce =
-            first->isCompare_Int32WithBoolean()
-            ? first->toCompare_Int32WithBoolean()
-            : ((second && second->isCompare_Int32WithBoolean())
-               ? second->toCompare_Int32WithBoolean()
-               : nullptr);
-        if (coerce) {
-            return coerce->lhsIsInt32()
-                   ? MCompare::Compare_Int32MaybeCoerceRHS
-                   : MCompare::Compare_Int32MaybeCoerceLHS;
-        }
-        return MCompare::Compare_Int32;
-    }
+    MCompare::CompareType first_type = ParseCacheIRStubForCompareType(first->toCacheIR_Regular());
+    if (!second)
+        return first_type;
+
+    MCompare::CompareType second_type = ParseCacheIRStubForCompareType(second->toCacheIR_Regular());
 
-    if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) {
-        ICCompare_NumberWithUndefined* coerce =
-            first->isCompare_NumberWithUndefined()
-            ? first->toCompare_NumberWithUndefined()
-            : (second && second->isCompare_NumberWithUndefined())
-              ? second->toCompare_NumberWithUndefined()
-              : nullptr;
-        if (coerce) {
-            return coerce->lhsIsUndefined()
-                   ? MCompare::Compare_DoubleMaybeCoerceLHS
-                   : MCompare::Compare_DoubleMaybeCoerceRHS;
-        }
-        return MCompare::Compare_Double;
-    }
+    if (first_type == MCompare::Compare_Unknown || second_type == MCompare::Compare_Unknown)
+        return MCompare::Compare_Unknown;
 
-    return MCompare::Compare_Unknown;
+    if (first_type == second_type)
+        return first_type;
+
+    return CompatibleType(first_type, second_type);
 }
 
 static bool
 TryToSpecializeBinaryArithOp(ICStub** stubs,
                              uint32_t nstubs,
                              MIRType* result)
 {
     DebugOnly<bool> sawInt32 = false;
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -32,16 +32,43 @@ const char* js::jit::CacheKindNames[] = 
 #undef DEFINE_KIND
 };
 
 void
 CacheIRWriter::assertSameCompartment(JSObject* obj) {
     assertSameCompartmentDebugOnly(cx_, obj);
 }
 
+StubField
+CacheIRWriter::readStubFieldForIon(uint32_t offset, StubField::Type type) const
+{
+    size_t index = 0;
+    size_t currentOffset = 0;
+
+    // If we've seen an offset earlier than this before, we know we can start the search
+    // there at least, otherwise, we start the search from the beginning.
+    if (lastOffset_ < offset) {
+        currentOffset = lastOffset_;
+        index = lastIndex_;
+    }
+
+    while (currentOffset != offset) {
+        currentOffset += StubField::sizeInBytes(stubFields_[index].type());
+        index++;
+        MOZ_ASSERT(index < stubFields_.length());
+    }
+
+    MOZ_ASSERT(stubFields_[index].type() == type);
+
+    lastOffset_ = currentOffset;
+    lastIndex_ = index;
+
+    return stubFields_[index];
+}
+
 IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
                          ICState::Mode mode)
   : writer(cx),
     cx_(cx),
     script_(script),
     pc_(pc),
     cacheKind_(cacheKind),
     mode_(mode)
@@ -4809,53 +4836,193 @@ CompareIRGenerator::tryAttachStrictDiffe
     writer.loadBooleanResult(op_ == JSOP_STRICTNE ? true : false);
     writer.returnFromIC();
 
     trackAttached("StrictDifferentTypes");
     return true;
 }
 
 bool
+CompareIRGenerator::tryAttachInt32(ValOperandId lhsId, ValOperandId rhsId)
+{
+    if ((!lhsVal_.isInt32() && !lhsVal_.isBoolean()) ||
+        (!rhsVal_.isInt32() && !rhsVal_.isBoolean()))
+    {
+        return false;
+    }
+
+    Int32OperandId lhsIntId = lhsVal_.isBoolean() ? writer.guardIsBoolean(lhsId)
+                                                  : writer.guardIsInt32(lhsId);
+    Int32OperandId rhsIntId = rhsVal_.isBoolean() ? writer.guardIsBoolean(rhsId)
+                                                  : writer.guardIsInt32(rhsId);
+
+    // Strictly different types should have been handed by
+    // tryAttachStrictDifferentTypes
+    MOZ_ASSERT_IF(op_ == JSOP_STRICTEQ || op_ == JSOP_STRICTNE,
+                  lhsVal_.isInt32() == rhsVal_.isInt32());
+
+    writer.compareInt32Result(op_, lhsIntId, rhsIntId);
+    writer.returnFromIC();
+
+    trackAttached(lhsVal_.isBoolean() ? "Boolean" : "Int32");
+    return true;
+}
+
+bool
+CompareIRGenerator::tryAttachNumber(ValOperandId lhsId, ValOperandId rhsId)
+{
+    if (!cx_->runtime()->jitSupportsFloatingPoint)
+        return false;
+
+    if (!lhsVal_.isNumber() || !rhsVal_.isNumber())
+        return false;
+
+    writer.guardIsNumber(lhsId);
+    writer.guardIsNumber(rhsId);
+    writer.compareDoubleResult(op_, lhsId, rhsId);
+    writer.returnFromIC();
+
+    trackAttached("Number");
+    return true;
+}
+
+bool
+CompareIRGenerator::tryAttachObjectUndefined(ValOperandId lhsId, ValOperandId rhsId)
+{
+    if (!(lhsVal_.isNullOrUndefined() && rhsVal_.isObject()) &&
+        !(rhsVal_.isNullOrUndefined() && lhsVal_.isObject()))
+        return false;
+
+    if (op_ != JSOP_EQ && op_ != JSOP_NE)
+        return false;
+
+    ValOperandId obj = rhsVal_.isObject() ? rhsId : lhsId;
+    ValOperandId undefOrNull = rhsVal_.isObject() ? lhsId : rhsId;
+
+    writer.guardIsNullOrUndefined(undefOrNull);
+    ObjOperandId objOperand = writer.guardIsObject(obj);
+    writer.compareObjectUndefinedNullResult(op_, objOperand);
+    writer.returnFromIC();
+
+    trackAttached("ObjectUndefined");
+    return true;
+}
+
+// Handle NumberUndefined comparisons
+bool
+CompareIRGenerator::tryAttachNumberUndefined(ValOperandId lhsId, ValOperandId rhsId)
+{
+    if (!(lhsVal_.isUndefined() && rhsVal_.isNumber()) &&
+        !(rhsVal_.isUndefined() && lhsVal_.isNumber()))
+    {
+        return false;
+    }
+
+    lhsVal_.isNumber() ? writer.guardIsNumber(lhsId) : writer.guardIsUndefined(lhsId);
+    rhsVal_.isNumber() ? writer.guardIsNumber(rhsId) : writer.guardIsUndefined(rhsId);
+
+    // Comparing a number with undefined will always be true for NE/STRICTNE,
+    // and always be false for other compare ops.
+    writer.loadBooleanResult(op_ == JSOP_NE || op_ == JSOP_STRICTNE);
+    writer.returnFromIC();
+
+    trackAttached("NumberUndefined");
+    return true;
+}
+
+// Handle {null/undefined} x {null,undefined} equality comparisons
+bool
+CompareIRGenerator::tryAttachNullUndefined(ValOperandId lhsId, ValOperandId rhsId)
+{
+    if (!lhsVal_.isNullOrUndefined() || !rhsVal_.isNullOrUndefined())
+        return false;
+
+    if (op_ == JSOP_EQ || op_ == JSOP_NE) {
+        writer.guardIsNullOrUndefined(lhsId);
+        writer.guardIsNullOrUndefined(rhsId);
+        // Sloppy equality means we actually only care about the op:
+        writer.loadBooleanResult(op_ == JSOP_EQ);
+        trackAttached("SloppyNullUndefined");
+    } else {
+        // Strict equality only hits this branch, and only in the
+        // undef {!,=}==  undef and null {!,=}== null cases.
+        // The other cases should have hit compareStrictlyDifferentTypes.
+        MOZ_ASSERT(lhsVal_.isNull() == rhsVal_.isNull());
+        lhsVal_.isNull() ? writer.guardIsNull(lhsId) : writer.guardIsUndefined(lhsId);
+        rhsVal_.isNull() ? writer.guardIsNull(rhsId) : writer.guardIsUndefined(rhsId);
+        writer.loadBooleanResult(op_ == JSOP_STRICTEQ);
+        trackAttached("StrictNullUndefinedEquality");
+    }
+
+    writer.returnFromIC();
+    return true;
+}
+
+bool
 CompareIRGenerator::tryAttachStub()
 {
     MOZ_ASSERT(cacheKind_ == CacheKind::Compare);
     MOZ_ASSERT(IsEqualityOp(op_) ||
                op_ == JSOP_LE || op_ == JSOP_LT ||
                op_ == JSOP_GE || op_ == JSOP_GT);
 
     AutoAssertNoPendingException aanpe(cx_);
 
+    constexpr uint8_t lhsIndex = 0;
+    constexpr uint8_t rhsIndex = 1;
+
+    static_assert(lhsIndex == 0 && rhsIndex == 1,
+        "Indexes relied upon by baseline inspector");
+
     ValOperandId lhsId(writer.setInputOperandId(0));
     ValOperandId rhsId(writer.setInputOperandId(1));
 
     if (IsEqualityOp(op_)) {
         if (tryAttachString(lhsId, rhsId))
             return true;
         if (tryAttachObject(lhsId, rhsId))
             return true;
         if (tryAttachSymbol(lhsId, rhsId))
             return true;
+        if (tryAttachObjectUndefined(lhsId, rhsId))
+            return true;
         if (tryAttachStrictDifferentTypes(lhsId, rhsId))
             return true;
 
-        trackAttached(IRGenerator::NotAttached);
-        return false;
+        // This should come after strictDifferent types to
+        // allow it to only handle sloppy equality.
+        if (tryAttachNullUndefined(lhsId, rhsId))
+            return true;
     }
 
+    // This should preceed the Int32/Number cases to allow
+    // them to not concern themselves with handling undefined
+    // or null.
+    if (tryAttachNumberUndefined(lhsId, rhsId))
+        return true;
+
+    // We want these to be last, to allow us to bypass the
+    // strictly-different-types cases in the below attachment code
+    if (tryAttachInt32(lhsId, rhsId))
+        return true;
+    if (tryAttachNumber(lhsId, rhsId))
+        return true;
+
     trackAttached(IRGenerator::NotAttached);
     return false;
 }
 
 void
 CompareIRGenerator::trackAttached(const char* name)
 {
 #ifdef JS_CACHEIR_SPEW
     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
         sp.valueProperty("lhs", lhsVal_);
         sp.valueProperty("rhs", rhsVal_);
+        sp.opcodeProperty("op", op_);
     }
 #endif
 }
 
 ToBoolIRGenerator::ToBoolIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
                                      HandleValue val)
   : IRGenerator(cx, script, pc, CacheKind::ToBool, mode),
     val_(val)
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -177,16 +177,19 @@ enum class CacheKind : uint8_t
 };
 
 extern const char* CacheKindNames[];
 
 #define CACHE_IR_OPS(_)                   \
     _(GuardIsObject)                      \
     _(GuardIsObjectOrNull)                \
     _(GuardIsNullOrUndefined)             \
+    _(GuardIsNotNullOrUndefined)          \
+    _(GuardIsNull)                        \
+    _(GuardIsUndefined)                   \
     _(GuardIsBoolean)                     \
     _(GuardIsString)                      \
     _(GuardIsSymbol)                      \
     _(GuardIsNumber)                      \
     _(GuardIsInt32)                       \
     _(GuardIsInt32Index)                  \
     _(GuardType)                          \
     _(GuardShape)                         \
@@ -319,16 +322,19 @@ extern const char* CacheKindNames[];
                                           \
     _(CallStringSplitResult)              \
     _(CallStringConcatResult)             \
     _(CallStringObjectConcatResult)       \
                                           \
     _(CompareStringResult)                \
     _(CompareObjectResult)                \
     _(CompareSymbolResult)                \
+    _(CompareInt32Result)                 \
+    _(CompareDoubleResult)                \
+    _(CompareObjectUndefinedNullResult)   \
                                           \
     _(CallPrintString)                    \
     _(Breakpoint)                         \
                                           \
     _(TypeMonitorResult)                  \
     _(ReturnFromIC)                       \
     _(WrapResult)
 
@@ -433,16 +439,20 @@ class MOZ_RAII CacheIRWriter : public JS
     Vector<uint32_t, 8, SystemAllocPolicy> operandLastUsed_;
 
     // OperandId and stub offsets are stored in a single byte, so make sure
     // this doesn't overflow. We use a very conservative limit for now.
     static const size_t MaxOperandIds = 20;
     static const size_t MaxStubDataSizeInBytes = 20 * sizeof(uintptr_t);
     bool tooLarge_;
 
+    // Basic caching to avoid quadatic lookup behaviour in readStubFieldForIon.
+    mutable uint32_t lastOffset_;
+    mutable uint32_t lastIndex_;
+
     void assertSameCompartment(JSObject*);
 
     void writeOp(CacheOp op) {
         MOZ_ASSERT(uint32_t(op) <= UINT8_MAX);
         buffer_.writeByte(uint32_t(op));
         nextInstructionId_++;
     }
 
@@ -496,17 +506,19 @@ class MOZ_RAII CacheIRWriter : public JS
   public:
     explicit CacheIRWriter(JSContext* cx)
       : CustomAutoRooter(cx),
         cx_(cx),
         nextOperandId_(0),
         nextInstructionId_(0),
         numInputOperands_(0),
         stubDataSize_(0),
-        tooLarge_(false)
+        tooLarge_(false),
+        lastOffset_(0),
+        lastIndex_(0)
     {}
 
     bool failed() const { return buffer_.oom() || tooLarge_; }
 
     uint32_t numInputOperands() const { return numInputOperands_; }
     uint32_t numOperandIds() const { return nextOperandId_; }
     uint32_t numInstructions() const { return nextInstructionId_; }
 
@@ -546,20 +558,17 @@ class MOZ_RAII CacheIRWriter : public JS
     }
     uint32_t codeLength() const {
         MOZ_ASSERT(!failed());
         return buffer_.length();
     }
 
     // This should not be used when compiling Baseline code, as Baseline code
     // shouldn't bake in stub values.
-    StubField readStubFieldForIon(size_t i, StubField::Type type) const {
-        MOZ_ASSERT(stubFields_[i].type() == type);
-        return stubFields_[i];
-    }
+    StubField readStubFieldForIon(uint32_t offset, StubField::Type type) const;
 
     ObjOperandId guardIsObject(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsObject, val);
         return ObjOperandId(val.id());
     }
     Int32OperandId guardIsBoolean(ValOperandId val) {
         Int32OperandId res(nextOperandId_++);
         writeOpWithOperandId(CacheOp::GuardIsBoolean, val);
@@ -595,16 +604,25 @@ class MOZ_RAII CacheIRWriter : public JS
         buffer_.writeByte(uint32_t(type));
     }
     void guardIsObjectOrNull(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsObjectOrNull, val);
     }
     void guardIsNullOrUndefined(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsNullOrUndefined, val);
     }
+    void guardIsNotNullOrUndefined(ValOperandId val) {
+        writeOpWithOperandId(CacheOp::GuardIsNotNullOrUndefined, val);
+    }
+    void guardIsNull(ValOperandId val) {
+        writeOpWithOperandId(CacheOp::GuardIsNull, val);
+    }
+    void guardIsUndefined(ValOperandId val) {
+        writeOpWithOperandId(CacheOp::GuardIsUndefined, val);
+    }
     void guardShape(ObjOperandId obj, Shape* shape) {
         MOZ_ASSERT(shape);
         writeOpWithOperandId(CacheOp::GuardShape, obj);
         addStubField(uintptr_t(shape), StubField::Type::Shape);
     }
     void guardShapeForClass(ObjOperandId obj, Shape* shape) {
         // Guard shape to ensure that object class is unchanged. This is true
         // for all shapes.
@@ -1252,21 +1270,35 @@ class MOZ_RAII CacheIRWriter : public JS
         writeOperandId(rhs);
         buffer_.writeByte(uint32_t(op));
     }
     void compareObjectResult(uint32_t op, ObjOperandId lhs, ObjOperandId rhs) {
         writeOpWithOperandId(CacheOp::CompareObjectResult, lhs);
         writeOperandId(rhs);
         buffer_.writeByte(uint32_t(op));
     }
+    void compareObjectUndefinedNullResult(uint32_t op, ObjOperandId object) {
+        writeOpWithOperandId(CacheOp::CompareObjectUndefinedNullResult, object);
+        buffer_.writeByte(uint32_t(op));
+    }
     void compareSymbolResult(uint32_t op, SymbolOperandId lhs, SymbolOperandId rhs) {
         writeOpWithOperandId(CacheOp::CompareSymbolResult, lhs);
         writeOperandId(rhs);
         buffer_.writeByte(uint32_t(op));
     }
+    void compareInt32Result(uint32_t op, Int32OperandId lhs, Int32OperandId rhs) {
+        writeOpWithOperandId(CacheOp::CompareInt32Result, lhs);
+        writeOperandId(rhs);
+        buffer_.writeByte(uint32_t(op));
+    }
+    void compareDoubleResult(uint32_t op, ValOperandId lhs, ValOperandId rhs) {
+        writeOpWithOperandId(CacheOp::CompareDoubleResult, lhs);
+        writeOperandId(rhs);
+        buffer_.writeByte(uint32_t(op));
+    }
 
     void callPrintString(const char* str) {
         writeOp(CacheOp::CallPrintString);
         writePointer(const_cast<char*>(str));
     }
     void breakpoint() {
         writeOp(CacheOp::Breakpoint);
     }
@@ -1811,16 +1843,21 @@ class MOZ_RAII CompareIRGenerator : publ
     JSOp op_;
     HandleValue lhsVal_;
     HandleValue rhsVal_;
 
     bool tryAttachString(ValOperandId lhsId, ValOperandId rhsId);
     bool tryAttachObject(ValOperandId lhsId, ValOperandId rhsId);
     bool tryAttachSymbol(ValOperandId lhsId, ValOperandId rhsId);
     bool tryAttachStrictDifferentTypes(ValOperandId lhsId, ValOperandId rhsId);
+    bool tryAttachInt32(ValOperandId lhsId, ValOperandId rhsId);
+    bool tryAttachNumber(ValOperandId lhsId, ValOperandId rhsId);
+    bool tryAttachNumberUndefined(ValOperandId lhsId, ValOperandId rhsId);
+    bool tryAttachObjectUndefined(ValOperandId lhsId, ValOperandId rhsId);
+    bool tryAttachNullUndefined(ValOperandId lhsId, ValOperandId rhsId);
 
     void trackAttached(const char* name);
 
   public:
     CompareIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode,
                        JSOp op, HandleValue lhsVal, HandleValue rhsVal);
 
     bool tryAttachStub();
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -88,17 +88,17 @@ CacheRegisterAllocator::useValueRegister
     }
 
     MOZ_CRASH();
 }
 
 // Load a value operand directly into a float register. Caller must have
 // guarded isNumber on the provided val.
 void
-CacheRegisterAllocator::loadDouble(MacroAssembler& masm, ValOperandId op, FloatRegister dest)
+CacheRegisterAllocator::ensureDoubleRegister(MacroAssembler& masm, ValOperandId op, FloatRegister dest)
 {
     OperandLocation& loc = operandLocations_[op.id()];
 
     Label failure, done;
     switch (loc.kind()) {
       case OperandLocation::ValueReg: {
         masm.ensureDouble(loc.valueReg(), dest, &failure);
         break;
@@ -120,22 +120,22 @@ CacheRegisterAllocator::loadDouble(Macro
         loc.setDoubleReg(dest);
         return;
       }
 
       case OperandLocation::Constant:
       case OperandLocation::PayloadStack:
       case OperandLocation::PayloadReg:
       case OperandLocation::Uninitialized:
-        MOZ_CRASH("Unhandled operand type in loadDouble");
+        MOZ_CRASH("Unhandled operand type in ensureDoubleRegister");
         return;
     }
     masm.jump(&done);
     masm.bind(&failure);
-    masm.assumeUnreachable("Missing guard allowed non-number to hit loadDouble");
+    masm.assumeUnreachable("Missing guard allowed non-number to hit ensureDoubleRegister");
     masm.bind(&done);
 }
 
 
 ValueOperand
 CacheRegisterAllocator::useFixedValueRegister(MacroAssembler& masm, ValOperandId valId,
                                               ValueOperand reg)
 {
@@ -1372,16 +1372,73 @@ CacheIRCompiler::emitGuardIsNullOrUndefi
     Label success;
     masm.branchTestNull(Assembler::Equal, input, &success);
     masm.branchTestUndefined(Assembler::NotEqual, input, failure->label());
 
     masm.bind(&success);
     return true;
 }
 
+
+bool
+CacheIRCompiler::emitGuardIsNotNullOrUndefined()
+{
+    ValOperandId inputId = reader.valOperandId();
+    JSValueType knownType = allocator.knownType(inputId);
+    if (knownType == JSVAL_TYPE_UNDEFINED || knownType == JSVAL_TYPE_NULL)
+        return false;
+
+    ValueOperand input = allocator.useValueRegister(masm, inputId);
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    masm.branchTestNull(Assembler::Equal, input, failure->label());
+    masm.branchTestUndefined(Assembler::Equal, input, failure->label());
+
+    return true;
+}
+
+
+bool
+CacheIRCompiler::emitGuardIsNull()
+{
+    ValOperandId inputId = reader.valOperandId();
+    JSValueType knownType = allocator.knownType(inputId);
+    if (knownType == JSVAL_TYPE_NULL)
+        return true;
+
+    ValueOperand input = allocator.useValueRegister(masm, inputId);
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    Label success;
+    masm.branchTestNull(Assembler::NotEqual, input, failure->label());
+    return true;
+}
+
+bool
+CacheIRCompiler::emitGuardIsUndefined()
+{
+    ValOperandId inputId = reader.valOperandId();
+    JSValueType knownType = allocator.knownType(inputId);
+    if (knownType == JSVAL_TYPE_UNDEFINED)
+        return true;
+
+    ValueOperand input = allocator.useValueRegister(masm, inputId);
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    masm.branchTestUndefined(Assembler::NotEqual, input, failure->label());
+    return true;
+}
+
+
 bool
 CacheIRCompiler::emitGuardIsObjectOrNull()
 {
     ValOperandId inputId = reader.valOperandId();
     JSValueType knownType = allocator.knownType(inputId);
     if (knownType == JSVAL_TYPE_OBJECT || knownType == JSVAL_TYPE_NULL)
         return true;
 
@@ -1989,71 +2046,71 @@ CacheIRCompiler::emitLoadInt32ArrayLengt
 bool
 CacheIRCompiler::emitDoubleAddResult()
 {
     AutoOutputRegister output(*this);
 
     // Float register must be preserved. The BinaryArith ICs use
     // the fact that baseline has them available, as well as fixed temps on
     // LBinaryCache.
-    allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
-    allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
 
     masm.addDouble(FloatReg1, FloatReg0);
     masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
 
     return true;
 }
 bool
 CacheIRCompiler::emitDoubleSubResult()
 {
     AutoOutputRegister output(*this);
 
-    allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
-    allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
 
     masm.subDouble(FloatReg1, FloatReg0);
     masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
 
     return true;
 }
 bool
 CacheIRCompiler::emitDoubleMulResult()
 {
     AutoOutputRegister output(*this);
 
-    allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
-    allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
 
     masm.mulDouble(FloatReg1, FloatReg0);
     masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
 
     return true;
 }
 bool
 CacheIRCompiler::emitDoubleDivResult()
 {
     AutoOutputRegister output(*this);
 
-    allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
-    allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
 
     masm.divDouble(FloatReg1, FloatReg0);
     masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
 
     return true;
 }
 bool
 CacheIRCompiler::emitDoubleModResult()
 {
     AutoOutputRegister output(*this);
     AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
 
-    allocator.loadDouble(masm, reader.valOperandId(), FloatReg0);
-    allocator.loadDouble(masm, reader.valOperandId(), FloatReg1);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
 
     LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
     masm.PushRegsInMask(save);
 
 
     masm.setupUnalignedABICall(scratch);
     masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
     masm.passABIArg(FloatReg1, MoveOp::DOUBLE);
@@ -3088,16 +3145,89 @@ CacheIRCompiler::emitCompareObjectResult
 
 bool
 CacheIRCompiler::emitCompareSymbolResult()
 {
     return emitComparePointerResultShared(true);
 }
 
 bool
+CacheIRCompiler::emitCompareInt32Result()
+{
+    AutoOutputRegister output(*this);
+    Register left = allocator.useRegister(masm, reader.int32OperandId());
+    Register right = allocator.useRegister(masm, reader.int32OperandId());
+    JSOp op = reader.jsop();
+
+    Label ifTrue, done;
+    masm.branch32(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue);
+
+    masm.moveValue(BooleanValue(false), output.valueReg());
+    masm.jump(&done);
+
+    masm.bind(&ifTrue);
+    masm.moveValue(BooleanValue(true), output.valueReg());
+    masm.bind(&done);
+    return true;
+}
+
+bool
+CacheIRCompiler::emitCompareDoubleResult()
+{
+    AutoOutputRegister output(*this);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg0);
+    allocator.ensureDoubleRegister(masm, reader.valOperandId(), FloatReg1);
+    JSOp op = reader.jsop();
+
+    Label done, ifTrue;
+    masm.branchDouble(JSOpToDoubleCondition(op), FloatReg0, FloatReg1, &ifTrue);
+    masm.moveValue(BooleanValue(false), output.valueReg());
+    masm.jump(&done);
+
+    masm.bind(&ifTrue);
+    masm.moveValue(BooleanValue(true), output.valueReg());
+    masm.bind(&done);
+    return true;
+}
+
+bool
+CacheIRCompiler::emitCompareObjectUndefinedNullResult()
+{
+    AutoOutputRegister output(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    JSOp op = reader.jsop();
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) {
+        // obj !== undefined/null for all objects.
+        masm.moveValue(BooleanValue(op == JSOP_STRICTNE), output.valueReg());
+    } else {
+        MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE);
+        AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
+        Label done, emulatesUndefined;
+        masm.branchIfObjectEmulatesUndefined(obj, scratch, failure->label(), &emulatesUndefined);
+        masm.moveValue(BooleanValue(op == JSOP_NE), output.valueReg());
+        masm.jump(&done);
+        masm.bind(&emulatesUndefined);
+        masm.moveValue(BooleanValue(op == JSOP_EQ), output.valueReg());
+        masm.bind(&done);
+    }
+    return true;
+}
+
+bool
 CacheIRCompiler::emitCallPrintString()
 {
     const char* str = reinterpret_cast<char*>(reader.pointer());
     masm.printf(str);
     return true;
 }
 
 bool
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -14,16 +14,19 @@
 namespace js {
 namespace jit {
 
 // The ops below are defined in CacheIRCompiler and codegen is shared between
 // BaselineCacheIRCompiler and IonCacheIRCompiler.
 #define CACHE_IR_SHARED_OPS(_)            \
     _(GuardIsObject)                      \
     _(GuardIsNullOrUndefined)             \
+    _(GuardIsNotNullOrUndefined)          \
+    _(GuardIsNull)                        \
+    _(GuardIsUndefined)                   \
     _(GuardIsObjectOrNull)                \
     _(GuardIsBoolean)                     \
     _(GuardIsString)                      \
     _(GuardIsSymbol)                      \
     _(GuardIsNumber)                      \
     _(GuardIsInt32)                       \
     _(GuardIsInt32Index)                  \
     _(GuardType)                          \
@@ -88,16 +91,19 @@ namespace jit {
     _(LoadObjectResult)                   \
     _(LoadTypeOfObjectResult)             \
     _(LoadInt32TruthyResult)              \
     _(LoadDoubleTruthyResult)             \
     _(LoadStringTruthyResult)             \
     _(LoadObjectTruthyResult)             \
     _(CompareObjectResult)                \
     _(CompareSymbolResult)                \
+    _(CompareInt32Result)                 \
+    _(CompareDoubleResult)                \
+    _(CompareObjectUndefinedNullResult)   \
     _(ArrayJoinResult)                    \
     _(CallPrintString)                    \
     _(Breakpoint)                         \
     _(MegamorphicLoadSlotResult)          \
     _(MegamorphicLoadSlotByValueResult)   \
     _(MegamorphicStoreSlot)               \
     _(MegamorphicHasPropResult)           \
     _(CallObjectHasSparseElementResult)   \
@@ -483,18 +489,20 @@ class MOZ_RAII CacheRegisterAllocator
     Register useRegister(MacroAssembler& masm, TypedOperandId typedId);
 
     ConstantOrRegister useConstantOrRegister(MacroAssembler& masm, ValOperandId val);
 
     // Allocates an output register for the given operand.
     Register defineRegister(MacroAssembler& masm, TypedOperandId typedId);
     ValueOperand defineValueRegister(MacroAssembler& masm, ValOperandId val);
 
-    // Loads (and unboxes) a value into a float register (caller guarded)
-    void loadDouble(MacroAssembler&, ValOperandId, FloatRegister);
+    // Loads (potentially coercing) and unboxes a value into a float register
+    // This is infallible, as there should have been a previous guard
+    // to ensure the ValOperandId is already a number.
+    void ensureDoubleRegister(MacroAssembler&, ValOperandId, FloatRegister);
 
     // Returns |val|'s JSValueType or JSVAL_TYPE_UNKNOWN.
     JSValueType knownType(ValOperandId val) const;
 
     // Emits code to restore registers and stack to the state at the start of
     // the stub.
     void restoreInputState(MacroAssembler& masm, bool discardStack = true);
 
@@ -629,34 +637,31 @@ class MOZ_RAII CacheIRCompiler
 
     // Whether this IC may read double values from uint32 arrays.
     mozilla::Maybe<bool> allowDoubleResult_;
 
     // Distance from the IC to the stub data; mostly will be
     // sizeof(stubType)
     uint32_t stubDataOffset_;
 
-    uint32_t nextStubField_;
-
     enum class StubFieldPolicy {
         Address,
         Constant
     };
 
     StubFieldPolicy stubFieldPolicy_;
 
     CacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, uint32_t stubDataOffset, Mode mode, StubFieldPolicy policy)
       : cx_(cx),
         reader(writer),
         writer_(writer),
         allocator(writer_),
         liveFloatRegs_(FloatRegisterSet::All()),
         mode_(mode),
         stubDataOffset_(stubDataOffset),
-        nextStubField_(0),
         stubFieldPolicy_(policy)
     {
         MOZ_ASSERT(!writer.failed());
     }
 
     MOZ_MUST_USE bool addFailurePath(FailurePath** failure);
     MOZ_MUST_USE bool emitFailurePath(size_t i);
 
@@ -711,25 +716,22 @@ class MOZ_RAII CacheIRCompiler
 #undef DEFINE_SHARED_OP
 
     void emitLoadStubField(StubFieldOffset val, Register dest);
     void emitLoadStubFieldConstant(StubFieldOffset val, Register dest);
 
     uintptr_t readStubWord(uint32_t offset, StubField::Type type) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
-        // We use nextStubField_ to access the data as it's stored in an as-of-yet
-        // unpacked vector, and so using the offset can be incorrect where the index
-        // would change as a result of packing.
-        return writer_.readStubFieldForIon(nextStubField_++, type).asWord();
+        return writer_.readStubFieldForIon(offset, type).asWord();
     }
     uint64_t readStubInt64(uint32_t offset, StubField::Type type) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
-        return writer_.readStubFieldForIon(nextStubField_++, type).asInt64();
+        return writer_.readStubFieldForIon(offset, type).asInt64();
     }
     int32_t int32StubField(uint32_t offset) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         return readStubWord(offset, StubField::Type::RawWord);
     }
     Shape* shapeStubField(uint32_t offset) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         return (Shape*)readStubWord(offset, StubField::Type::Shape);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -188,16 +188,21 @@ typedef bool (*IonUnaryArithICFn)(JSCont
 static const VMFunction IonUnaryArithICInfo =
     FunctionInfo<IonUnaryArithICFn>(IonUnaryArithIC::update, "IonUnaryArithIC::update");
 
 typedef bool (*IonBinaryArithICFn)(JSContext* cx, HandleScript outerScript, IonBinaryArithIC* stub,
                                     HandleValue lhs, HandleValue rhs, MutableHandleValue res);
 static const VMFunction IonBinaryArithICInfo =
     FunctionInfo<IonBinaryArithICFn>(IonBinaryArithIC::update, "IonBinaryArithIC::update");
 
+typedef bool (*IonCompareICFn)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub,
+                                    HandleValue lhs, HandleValue rhs, MutableHandleValue res);
+static const VMFunction IonCompareICInfo =
+    FunctionInfo<IonCompareICFn>(IonCompareIC::update, "IonCompareIC::update");
+
 void
 CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
 {
     LInstruction* lir = ool->lir();
     size_t cacheIndex = ool->cacheIndex();
     size_t cacheInfoIndex = ool->cacheInfoIndex();
 
     DataPtr<IonIC> ic(this, cacheIndex);
@@ -397,18 +402,34 @@ CodeGenerator::visitOutOfLineICFallback(
         callVM(IonBinaryArithICInfo, lir);
 
         StoreValueTo(binaryArithIC->output()).generate(this);
         restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered());
 
         masm.jump(ool->rejoin());
         return;
       }
+      case CacheKind::Compare: {
+        IonCompareIC* compareIC = ic->asCompareIC();
+
+        saveLive(lir);
+
+        pushArg(compareIC->rhs());
+        pushArg(compareIC->lhs());
+        icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
+        pushArg(ImmGCPtr(gen->info().script()));
+        callVM(IonCompareICInfo, lir);
+
+        StoreValueTo(compareIC->output()).generate(this);
+        restoreLiveIgnore(lir, StoreValueTo(compareIC->output()).clobbered());
+
+        masm.jump(ool->rejoin());
+        return;
+      }
       case CacheKind::Call:
-      case CacheKind::Compare:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
         MOZ_CRASH("Unsupported IC");
     }
     MOZ_CRASH();
 }
 
@@ -1417,16 +1438,49 @@ PrepareAndExecuteRegExp(JSContext* cx, M
                         Register temp1, Register temp2, Register temp3,
                         size_t inputOutputDataStartOffset,
                         RegExpShared::CompilationMode mode,
                         bool stringsCanBeInNursery,
                         Label* notFound, Label* failure)
 {
     JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp");
 
+    /*
+     * [SMDOC] Stack layout for PrepareAndExecuteRegExp
+     *
+     * inputOutputDataStartOffset +-----> +---------------+
+     *                                    |InputOutputData|
+     *          inputStartAddress +---------->  inputStart|
+     *            inputEndAddress +---------->    inputEnd|
+     *          startIndexAddress +---------->  startIndex|
+     *            endIndexAddress +---------->    endIndex|
+     *      matchesPointerAddress +---------->     matches|
+     *         matchResultAddress +---------->      result|
+     *                                    +---------------+
+     *      matchPairsStartOffset +-----> +---------------+
+     *                                    |  MatchPairs   |
+     *            pairCountAddress +----------->  count   |
+     *         pairsPointerAddress +----------->  pairs   |
+     *                                    |               |
+     *                                    +---------------+
+     *     pairsVectorStartOffset +-----> +---------------+
+     *                                    |   MatchPair   |
+     *                                    |       start   |  <-------+
+     *                                    |       limit   |          | Reserved space for
+     *                                    +---------------+          | `RegExpObject::MaxPairCount`
+     *                                           .                   | MatchPair objects.
+     *                                           .                   |
+     *                                           .                   |
+     *                                    +---------------+          |
+     *                                    |   MatchPair   |          |
+     *                                    |       start   |  <-------+
+     *                                    |       limit   |
+     *                                    +---------------+
+     */
+
     size_t matchPairsStartOffset = inputOutputDataStartOffset + sizeof(irregexp::InputOutputData);
     size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
 
     Address inputStartAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputStart));
     Address inputEndAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputEnd));
     Address matchesPointerAddress(masm.getStackPointer(),
@@ -1437,32 +1491,40 @@ PrepareAndExecuteRegExp(JSContext* cx, M
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, endIndex));
     Address matchResultAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, result));
 
     Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset);
     Address pairsPointerAddress(masm.getStackPointer(),
         matchPairsStartOffset + MatchPairs::offsetOfPairs());
 
-    Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset);
-
     RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global());
     if (!res)
         return false;
 #ifdef JS_USE_LINK_REGISTER
     if (mode != RegExpShared::MatchOnly)
         masm.pushReturnAddress();
 #endif
     if (mode == RegExpShared::Normal) {
         // First, fill in a skeletal MatchPairs instance on the stack. This will be
         // passed to the OOL stub in the caller if we aren't able to execute the
         // RegExp inline, and that stub needs to be able to determine whether the
         // execution finished successfully.
+
+        // Initialize MatchPairs::pairCount to 1, the correct value can only
+        // be determined after loading the RegExpShared.
         masm.store32(Imm32(1), pairCountAddress);
-        masm.store32(Imm32(-1), pairsVectorAddress);
+
+        // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch.
+        Address firstMatchPairStartAddress(masm.getStackPointer(),
+                                           pairsVectorStartOffset + offsetof(MatchPair, start));
+        masm.store32(Imm32(MatchPair::NoMatch), firstMatchPairStartAddress);
+
+        // Assign the MatchPairs::pairs pointer to the first MatchPair object.
+        Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset);
         masm.computeEffectiveAddress(pairsVectorAddress, temp1);
         masm.storePtr(temp1, pairsPointerAddress);
     }
 
     // Check for a linear input string.
     masm.branchIfRopeOrExternal(input, temp1, failure);
 
     // Get the RegExpShared for the RegExp.
@@ -1502,30 +1564,29 @@ PrepareAndExecuteRegExp(JSContext* cx, M
         // <> floor(x / 1024) * 1024 = SurrogateMin
         // <> (x >>> 10) << 10 = SurrogateMin
         // <> x & ~(2^10 - 1) = SurrogateMin
 
         constexpr char16_t SurrogateMask = 0xFC00;
 
         // Check if input[lastIndex] is trail surrogate.
         masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
-        masm.load16ZeroExtend(BaseIndex(temp2, lastIndex, TimesTwo), temp3);
+        masm.loadChar(temp2, lastIndex, temp3, CharEncoding::TwoByte);
 
         masm.and32(Imm32(SurrogateMask), temp3);
         masm.branch32(Assembler::NotEqual, temp3, Imm32(unicode::TrailSurrogateMin), &done);
 
         // Check if input[lastIndex-1] is lead surrogate.
-        masm.load16ZeroExtend(BaseIndex(temp2, lastIndex, TimesTwo, -int32_t(sizeof(char16_t))),
-                              temp3);
+        masm.loadChar(temp2, lastIndex, temp3, CharEncoding::TwoByte, -int32_t(sizeof(char16_t)));
 
         masm.and32(Imm32(SurrogateMask), temp3);
         masm.branch32(Assembler::NotEqual, temp3, Imm32(unicode::LeadSurrogateMin), &done);
 
         // Move lastIndex to lead surrogate.
-        masm.subPtr(Imm32(1), lastIndex);
+        masm.sub32(Imm32(1), lastIndex);
 
         masm.bind(&done);
     }
 
     if (mode == RegExpShared::Normal) {
         // Don't handle RegExps with excessive parens.
         masm.load32(Address(temp1, RegExpShared::offsetOfParenCount()), temp2);
         masm.branch32(Assembler::AboveOrEqual, temp2, Imm32(RegExpObject::MaxPairCount), failure);
@@ -1573,17 +1634,17 @@ PrepareAndExecuteRegExp(JSContext* cx, M
         masm.computeEffectiveAddress(Address(masm.getStackPointer(), matchPairsStartOffset), temp2);
         masm.storePtr(temp2, matchesPointerAddress);
     } else {
         // Use InputOutputData.endIndex itself for output.
         masm.computeEffectiveAddress(endIndexAddress, temp2);
         masm.storePtr(temp2, endIndexAddress);
     }
     masm.storePtr(lastIndex, startIndexAddress);
-    masm.store32(Imm32(0), matchResultAddress);
+    masm.store32(Imm32(RegExpRunStatus_Error), matchResultAddress);
 
     // Save any volatile inputs.
     LiveGeneralRegisterSet volatileRegs;
     if (lastIndex.volatile_())
         volatileRegs.add(lastIndex);
     if (input.volatile_())
         volatileRegs.add(input);
     if (regexp.volatile_())
@@ -1666,194 +1727,192 @@ PrepareAndExecuteRegExp(JSContext* cx, M
         masm.load32(endIndexAddress, temp3);
     }
 
     return true;
 }
 
 static void
 CopyStringChars(MacroAssembler& masm, Register to, Register from, Register len,
-                Register byteOpScratch, size_t fromWidth, size_t toWidth);
+                Register byteOpScratch, CharEncoding encoding);
 
 class CreateDependentString
 {
+    CharEncoding encoding_;
     Register string_;
-    Register temp_;
+    Register temp1_;
+    Register temp2_;
     Label* failure_;
+
     enum class FallbackKind : uint8_t {
         InlineString,
         FatInlineString,
         NotInlineString,
         Count
     };
     mozilla::EnumeratedArray<FallbackKind, FallbackKind::Count, Label> fallbacks_, joins_;
 
-public:
+  public:
+    CreateDependentString(CharEncoding encoding, Register string, Register temp1, Register temp2,
+                          Label* failure)
+      : encoding_(encoding), string_(string), temp1_(temp1), temp2_(temp2), failure_(failure)
+    { }
+
+    Register string() const { return string_; }
+    CharEncoding encoding() const { return encoding_; }
+
     // Generate code that creates DependentString.
     // Caller should call generateFallback after masm.ret(), to generate
     // fallback path.
     void generate(MacroAssembler& masm, const JSAtomState& names,
-                  CompileRuntime* runtime,
-                  bool latin1, Register string,
-                  Register base, Register temp1, Register temp2,
+                  CompileRuntime* runtime, Register base,
                   BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
-                  bool stringsCanBeInNursery,
-                  Label* failure);
+                  bool stringsCanBeInNursery);
 
     // Generate fallback path for creating DependentString.
-    void generateFallback(MacroAssembler& masm, LiveRegisterSet regsToSave);
+    void generateFallback(MacroAssembler& masm);
 };
 
 void
 CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names,
-                                CompileRuntime* runtime,
-                                bool latin1, Register string,
-                                Register base, Register temp1, Register temp2,
+                                CompileRuntime* runtime, Register base,
                                 BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
-                                bool stringsCanBeInNursery,
-                                Label* failure)
+                                bool stringsCanBeInNursery)
 {
     JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)",
-            (latin1 ? "Latin-1" : "Two-Byte"));
-
-    string_ = string;
-    temp_ = temp2;
-    failure_ = failure;
+            (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
+
+    auto newGCString = [&](FallbackKind kind) {
+        uint32_t flags = kind == FallbackKind::InlineString
+                         ? JSString::INIT_THIN_INLINE_FLAGS
+                         : kind == FallbackKind::FatInlineString
+                         ? JSString::INIT_FAT_INLINE_FLAGS
+                         : JSString::DEPENDENT_FLAGS;
+        if (encoding_ == CharEncoding::Latin1)
+            flags |= JSString::LATIN1_CHARS_BIT;
+
+        if (kind != FallbackKind::FatInlineString)
+            masm.newGCString(string_, temp2_, &fallbacks_[kind], stringsCanBeInNursery);
+        else
+            masm.newGCFatInlineString(string_, temp2_, &fallbacks_[kind], stringsCanBeInNursery);
+        masm.bind(&joins_[kind]);
+        masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags()));
+    };
 
     // Compute the string length.
-    masm.load32(startIndexAddress, temp2);
-    masm.load32(limitIndexAddress, temp1);
-    masm.sub32(temp2, temp1);
+    masm.load32(startIndexAddress, temp2_);
+    masm.load32(limitIndexAddress, temp1_);
+    masm.sub32(temp2_, temp1_);
 
     Label done, nonEmpty;
 
     // Zero length matches use the empty string.
-    masm.branchTest32(Assembler::NonZero, temp1, temp1, &nonEmpty);
-    masm.movePtr(ImmGCPtr(names.empty), string);
+    masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty);
+    masm.movePtr(ImmGCPtr(names.empty), string_);
     masm.jump(&done);
 
     masm.bind(&nonEmpty);
 
     Label notInline;
 
-    int32_t maxInlineLength = latin1
-                              ? (int32_t) JSFatInlineString::MAX_LENGTH_LATIN1
-                              : (int32_t) JSFatInlineString::MAX_LENGTH_TWO_BYTE;
-    masm.branch32(Assembler::Above, temp1, Imm32(maxInlineLength), &notInline);
-
+    int32_t maxInlineLength = encoding_ == CharEncoding::Latin1
+                              ? JSFatInlineString::MAX_LENGTH_LATIN1
+                              : JSFatInlineString::MAX_LENGTH_TWO_BYTE;
+    masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), &notInline);
     {
         // Make a thin or fat inline string.
         Label stringAllocated, fatInline;
 
-        int32_t maxThinInlineLength = latin1
-                                      ? (int32_t) JSThinInlineString::MAX_LENGTH_LATIN1
-                                      : (int32_t) JSThinInlineString::MAX_LENGTH_TWO_BYTE;
-        masm.branch32(Assembler::Above, temp1, Imm32(maxThinInlineLength), &fatInline);
-
-        int32_t thinFlags = (latin1 ? JSString::LATIN1_CHARS_BIT : 0) | JSString::INIT_THIN_INLINE_FLAGS;
-        masm.newGCString(string, temp2, &fallbacks_[FallbackKind::InlineString], stringsCanBeInNursery);
-        masm.bind(&joins_[FallbackKind::InlineString]);
-        masm.store32(Imm32(thinFlags), Address(string, JSString::offsetOfFlags()));
-        masm.jump(&stringAllocated);
-
+        int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1
+                                      ? JSThinInlineString::MAX_LENGTH_LATIN1
+                                      : JSThinInlineString::MAX_LENGTH_TWO_BYTE;
+        masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength), &fatInline);
+        {
+            newGCString(FallbackKind::InlineString);
+            masm.jump(&stringAllocated);
+        }
         masm.bind(&fatInline);
-
-        int32_t fatFlags = (latin1 ? JSString::LATIN1_CHARS_BIT : 0) | JSString::INIT_FAT_INLINE_FLAGS;
-        masm.newGCFatInlineString(string, temp2, &fallbacks_[FallbackKind::FatInlineString], stringsCanBeInNursery);
-        masm.bind(&joins_[FallbackKind::FatInlineString]);
-        masm.store32(Imm32(fatFlags), Address(string, JSString::offsetOfFlags()));
-
+        {
+            newGCString(FallbackKind::FatInlineString);
+        }
         masm.bind(&stringAllocated);
-        masm.store32(temp1, Address(string, JSString::offsetOfLength()));
-
-        masm.push(string);
+
+        masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
+
+        masm.push(string_);
         masm.push(base);
 
         // Adjust the start index address for the above pushes.
         MOZ_ASSERT(startIndexAddress.base == masm.getStackPointer());
         BaseIndex newStartIndexAddress = startIndexAddress;
         newStartIndexAddress.offset += 2 * sizeof(void*);
 
         // Load chars pointer for the new string.
-        masm.loadInlineStringCharsForStore(string, string);
+        masm.loadInlineStringCharsForStore(string_, string_);
 
         // Load the source characters pointer.
-        masm.loadStringChars(base, temp2,
-                             latin1 ? CharEncoding::Latin1 : CharEncoding::TwoByte);
+        masm.loadStringChars(base, temp2_, encoding_);
         masm.load32(newStartIndexAddress, base);
-        if (latin1)
-            masm.addPtr(temp2, base);
-        else
-            masm.computeEffectiveAddress(BaseIndex(temp2, base, TimesTwo), base);
-
-        CopyStringChars(masm, string, base, temp1, temp2, latin1 ? 1 : 2, latin1 ? 1 : 2);
+        masm.addToCharPtr(temp2_, base, encoding_);
+
+        CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_);
 
         // Null-terminate.
-        if (latin1)
-            masm.store8(Imm32(0), Address(string, 0));
-        else
-            masm.store16(Imm32(0), Address(string, 0));
+        masm.storeChar(Imm32(0), Address(string_, 0), encoding_);
 
         masm.pop(base);
-        masm.pop(string);
-    }
-
-    masm.jump(&done);
+        masm.pop(string_);
+
+        masm.jump(&done);
+    }
+
     masm.bind(&notInline);
 
     {
         // Make a dependent string.
-        int32_t flags = (latin1 ? JSString::LATIN1_CHARS_BIT : 0) | JSString::DEPENDENT_FLAGS;
-
-        masm.newGCString(string, temp2, &fallbacks_[FallbackKind::NotInlineString], stringsCanBeInNursery);
         // Warning: string may be tenured (if the fallback case is hit), so
         // stores into it must be post barriered.
-        masm.bind(&joins_[FallbackKind::NotInlineString]);
-        masm.store32(Imm32(flags), Address(string, JSString::offsetOfFlags()));
-        masm.store32(temp1, Address(string, JSString::offsetOfLength()));
-
-        masm.loadNonInlineStringChars(base, temp1,
-                                      latin1 ? CharEncoding::Latin1 : CharEncoding::TwoByte);
-        masm.load32(startIndexAddress, temp2);
-        if (latin1)
-            masm.addPtr(temp2, temp1);
-        else
-            masm.computeEffectiveAddress(BaseIndex(temp1, temp2, TimesTwo), temp1);
-        masm.storeNonInlineStringChars(temp1, string);
-        masm.storeDependentStringBase(base, string);
-        masm.movePtr(base, temp1);
+        newGCString(FallbackKind::NotInlineString);
+
+        masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
+
+        masm.loadNonInlineStringChars(base, temp1_, encoding_);
+        masm.load32(startIndexAddress, temp2_);
+        masm.addToCharPtr(temp1_, temp2_, encoding_);
+        masm.storeNonInlineStringChars(temp1_, string_);
+        masm.storeDependentStringBase(base, string_);
+        masm.movePtr(base, temp1_);
 
         // Follow any base pointer if the input is itself a dependent string.
         // Watch for undepended strings, which have a base pointer but don't
         // actually share their characters with it.
         Label noBase;
-        masm.load32(Address(base, JSString::offsetOfFlags()), temp2);
-        masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), temp2);
-        masm.branch32(Assembler::NotEqual, temp2, Imm32(JSString::DEPENDENT_FLAGS), &noBase);
-        masm.loadDependentStringBase(base, temp1);
-        masm.storeDependentStringBase(temp1, string);
+        masm.load32(Address(base, JSString::offsetOfFlags()), temp2_);
+        masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), temp2_);
+        masm.branch32(Assembler::NotEqual, temp2_, Imm32(JSString::DEPENDENT_FLAGS), &noBase);
+        masm.loadDependentStringBase(base, temp1_);
+        masm.storeDependentStringBase(temp1_, string_);
         masm.bind(&noBase);
 
         // Post-barrier the base store, whether it was the direct or indirect
         // base (both will end up in temp1 here).
-        masm.branchPtrInNurseryChunk(Assembler::Equal, string, temp2, &done);
-        masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp1, temp2, &done);
+        masm.branchPtrInNurseryChunk(Assembler::Equal, string_, temp2_, &done);
+        masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp1_, temp2_, &done);
 
         LiveRegisterSet regsToSave(RegisterSet::Volatile());
-        regsToSave.takeUnchecked(temp1);
-        regsToSave.takeUnchecked(temp2);
-        regsToSave.addUnchecked(string);
+        regsToSave.takeUnchecked(temp1_);
+        regsToSave.takeUnchecked(temp2_);
 
         masm.PushRegsInMask(regsToSave);
 
-        masm.mov(ImmPtr(runtime), temp1);
-
-        masm.setupUnalignedABICall(temp2);
-        masm.passABIArg(temp1);
-        masm.passABIArg(string);
+        masm.mov(ImmPtr(runtime), temp1_);
+
+        masm.setupUnalignedABICall(temp2_);
+        masm.passABIArg(temp1_);
+        masm.passABIArg(string_);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
 
         masm.PopRegsInMask(regsToSave);
     }
 
     masm.bind(&done);
 }
 
@@ -1867,22 +1926,25 @@ AllocateString(JSContext* cx)
 static void*
 AllocateFatInlineString(JSContext* cx)
 {
     AutoUnsafeCallWithABI unsafe;
     return js::Allocate<JSFatInlineString, NoGC>(cx, js::gc::TenuredHeap);
 }
 
 void
-CreateDependentString::generateFallback(MacroAssembler& masm, LiveRegisterSet regsToSave)
-{
-    JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString fallback");
-
-    regsToSave.take(string_);
-    regsToSave.take(temp_);
+CreateDependentString::generateFallback(MacroAssembler& masm)
+{
+    JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString fallback (encoding=%s)",
+            (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
+
+    LiveRegisterSet regsToSave(RegisterSet::Volatile());
+    regsToSave.takeUnchecked(string_);
+    regsToSave.takeUnchecked(temp2_);
+
     for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) {
         masm.bind(&fallbacks_[kind]);
 
         masm.PushRegsInMask(regsToSave);
 
         masm.setupUnalignedABICall(string_);
         masm.loadJSContext(string_);
         masm.passABIArg(string_);
@@ -1903,46 +1965,46 @@ static void*
 CreateMatchResultFallbackFunc(JSContext* cx, gc::AllocKind kind, size_t nDynamicSlots)
 {
     AutoUnsafeCallWithABI unsafe;
     return js::Allocate<JSObject, NoGC>(cx, kind, nDynamicSlots, gc::DefaultHeap,
                                         &ArrayObject::class_);
 }
 
 static void
-CreateMatchResultFallback(MacroAssembler& masm, LiveRegisterSet regsToSave,
-                          Register object, Register temp2, Register temp5,
-                          ArrayObject* templateObj, Label* fail)
+CreateMatchResultFallback(MacroAssembler& masm, Register object, Register temp1, Register temp2,
+                          const TemplateObject& templateObject, Label* fail)
 {
     JitSpew(JitSpew_Codegen, "# Emitting CreateMatchResult fallback");
 
-    MOZ_ASSERT(templateObj->group()->clasp() == &ArrayObject::class_);
-
-    regsToSave.take(object);
-    regsToSave.take(temp2);
-    regsToSave.take(temp5);
+    MOZ_ASSERT(templateObject.isArrayObject());
+
+    LiveRegisterSet regsToSave(RegisterSet::Volatile());
+    regsToSave.takeUnchecked(object);
+    regsToSave.takeUnchecked(temp1);
+    regsToSave.takeUnchecked(temp2);
+
     masm.PushRegsInMask(regsToSave);
 
     masm.setupUnalignedABICall(object);
 
     masm.loadJSContext(object);
     masm.passABIArg(object);
-    masm.move32(Imm32(int32_t(templateObj->asTenured().getAllocKind())), temp2);
+    masm.move32(Imm32(int32_t(templateObject.getAllocKind())), temp1);
+    masm.passABIArg(temp1);
+    masm.move32(Imm32(int32_t(templateObject.asNativeTemplateObject().numDynamicSlots())), temp2);
     masm.passABIArg(temp2);
-    masm.move32(Imm32(int32_t(templateObj->as<NativeObject>().numDynamicSlots())), temp5);
-    masm.passABIArg(temp5);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, CreateMatchResultFallbackFunc));
     masm.storeCallPointerResult(object);
 
     masm.PopRegsInMask(regsToSave);
 
     masm.branchPtr(Assembler::Equal, object, ImmWord(0), fail);
 
-    TemplateObject templateObject(templateObj);
-    masm.initGCThing(object, temp2, templateObject, true);
+    masm.initGCThing(object, temp1, templateObject, true);
 }
 
 JitCode*
 JitRealm::generateRegExpMatcherStub(JSContext* cx)
 {
     JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub");
 
     Register regexp = RegExpMatcherRegExpReg;
@@ -1951,224 +2013,214 @@ JitRealm::generateRegExpMatcherStub(JSCo
     ValueOperand result = JSReturnOperand;
 
     // We are free to clobber all registers, as LRegExpMatcher is a call instruction.
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
     regs.take(input);
     regs.take(regexp);
     regs.take(lastIndex);
 
-    // temp5 is used in single byte instructions when creating dependent
-    // strings, and has restrictions on which register it can be on some
-    // platforms.
-    Register temp5;
-    {
-        AllocatableGeneralRegisterSet oregs = regs;
-        do {
-            temp5 = oregs.takeAny();
-        } while (!MacroAssembler::canUseInSingleByteInstruction(temp5));
-        regs.take(temp5);
-    }
-
     Register temp1 = regs.takeAny();
     Register temp2 = regs.takeAny();
     Register temp3 = regs.takeAny();
-
-    Register maybeTemp4 = InvalidReg;
+    Register temp4 = regs.takeAny();
+    Register maybeTemp5 = InvalidReg;
     if (!regs.empty()) {
         // There are not enough registers on x86.
-        maybeTemp4 = regs.takeAny();
+        maybeTemp5 = regs.takeAny();
     }
 
     ArrayObject* templateObject = cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx);
     if (!templateObject)
         return nullptr;
+    TemplateObject templateObj(templateObject);
+    const NativeTemplateObject& nativeTemplateObj = templateObj.asNativeTemplateObject();
 
     // The template object should have enough space for the maximum number of
     // pairs this stub can handle.
     MOZ_ASSERT(ObjectElements::VALUES_PER_HEADER + RegExpObject::MaxPairCount ==
-               gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()));
+               gc::GetGCKindSlots(templateObj.getAllocKind()));
 
     StackMacroAssembler masm(cx);
 
     // The InputOutputData is placed above the return address on the stack.
     size_t inputOutputDataStartOffset = sizeof(void*);
 
     Label notFound, oolEntry;
     if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
-                                 temp1, temp2, temp5, inputOutputDataStartOffset,
+                                 temp1, temp2, temp3, inputOutputDataStartOffset,
                                  RegExpShared::Normal, stringsCanBeInNursery, &notFound, &oolEntry))
     {
         return nullptr;
     }
 
     // Construct the result.
     Register object = temp1;
     Label matchResultFallback, matchResultJoin;
-    TemplateObject templateObj(templateObject);
     masm.createGCObject(object, temp2, templateObj, gc::DefaultHeap, &matchResultFallback);
     masm.bind(&matchResultJoin);
 
     // Initialize slots of result object.
+    MOZ_ASSERT(nativeTemplateObj.numFixedSlots() == 0);
+    MOZ_ASSERT(nativeTemplateObj.numDynamicSlots() == 2);
+    static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0,
+                  "First slot holds the 'index' property");
+    static_assert(RegExpRealm::MatchResultObjectInputSlot == 1,
+                  "Second slot holds the 'input' property");
+
     masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
-    masm.storeValue(templateObject->getSlot(0), Address(temp2, 0));
-    masm.storeValue(templateObject->getSlot(1), Address(temp2, sizeof(Value)));
-
-    size_t elementsOffset = NativeObject::offsetOfFixedElements();
-
-#ifdef DEBUG
-    // Assert the initial value of initializedLength and length to make sure
-    // restoration on failure case works.
-    {
-        Label initLengthOK, lengthOK;
-        masm.branch32(Assembler::Equal,
-                      Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()),
-                      Imm32(templateObject->getDenseInitializedLength()),
-                      &initLengthOK);
-        masm.assumeUnreachable("Initial value of the match object's initializedLength does not match to restoration.");
-        masm.bind(&initLengthOK);
-
-        masm.branch32(Assembler::Equal,
-                      Address(object, elementsOffset + ObjectElements::offsetOfLength()),
-                      Imm32(templateObject->length()),
-                      &lengthOK);
-        masm.assumeUnreachable("Initial value of The match object's length does not match to restoration.");
-        masm.bind(&lengthOK);
-    }
-#endif
-
+    masm.storeValue(nativeTemplateObj.getSlot(RegExpRealm::MatchResultObjectIndexSlot),
+                    Address(temp2, 0));
+    masm.storeValue(nativeTemplateObj.getSlot(RegExpRealm::MatchResultObjectInputSlot),
+                    Address(temp2, sizeof(Value)));
+
+   /*
+    * [SMDOC] Stack layout for the RegExpMatcher stub
+    *
+    *                                    +---------------+
+    *                                    |Return-Address |
+    *                                    +---------------+
+    * inputOutputDataStartOffset +-----> +---------------+
+    *                                    |InputOutputData|
+    *                                    +---------------+
+    *                                    +---------------+
+    *                                    |  MatchPairs   |
+    *           pairsCountAddress +----------->  count   |
+    *                                    |       pairs   |
+    *                                    |               |
+    *                                    +---------------+
+    *     pairsVectorStartOffset +-----> +---------------+
+    *                                    |   MatchPair   |
+    *             matchPairStart +------------>  start   |  <-------+
+    *             matchPairLimit +------------>  limit   |          | Reserved space for
+    *                                    +---------------+          | `RegExpObject::MaxPairCount`
+    *                                           .                   | MatchPair objects.
+    *                                           .                   |
+    *                                           .                   | `count` objects will be
+    *                                    +---------------+          | initialized and can be
+    *                                    |   MatchPair   |          | accessed below.
+    *                                    |       start   |  <-------+
+    *                                    |       limit   |
+    *                                    +---------------+
+    */
+
+    static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t),
+                  "MatchPair consists of two int32 values representing the start"
+                  "and the end offset of the match");
+
+    Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset);
+
+    size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
+    Address firstMatchPairStartAddress(masm.getStackPointer(),
+                                       pairsVectorStartOffset + offsetof(MatchPair, start));
+
+    // Incremented by one below for each match pair.
     Register matchIndex = temp2;
     masm.move32(Imm32(0), matchIndex);
 
-    size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
-    Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset);
-    Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset);
-
-    BaseIndex stringAddress(object, matchIndex, TimesEight, elementsOffset);
-
-    JS_STATIC_ASSERT(sizeof(MatchPair) == 8);
-    BaseIndex stringIndexAddress(masm.getStackPointer(), matchIndex, TimesEight,
-                                 pairsVectorStartOffset + offsetof(MatchPair, start));
-    BaseIndex stringLimitAddress(masm.getStackPointer(), matchIndex, TimesEight,
-                                 pairsVectorStartOffset + offsetof(MatchPair, limit));
+    // The element in which to store the result of the current match.
+    size_t elementsOffset = NativeObject::offsetOfFixedElements();
+    BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset);
+
+    // The current match pair's "start" and "limit" member.
+    BaseIndex matchPairStart(masm.getStackPointer(), matchIndex, TimesEight,
+                             pairsVectorStartOffset + offsetof(MatchPair, start));
+    BaseIndex matchPairLimit(masm.getStackPointer(), matchIndex, TimesEight,
+                             pairsVectorStartOffset + offsetof(MatchPair, limit));
+
+    Register temp5;
+    if (maybeTemp5 == InvalidReg) {
+        // We don't have enough registers for a fifth temporary. Reuse
+        // |lastIndex| as a temporary. We don't need to restore its value,
+        // because |lastIndex| is no longer used after a successful match.
+        // (Neither here nor in the OOL path, cf. js::RegExpMatcherRaw.)
+        temp5 = lastIndex;
+    } else {
+        temp5 = maybeTemp5;
+    }
 
     // Loop to construct the match strings. There are two different loops,
-    // depending on whether the input is latin1.
-    CreateDependentString depStr[2];
-
-    // depStr may refer to failureRestore during generateFallback below,
-    // so this variable must live outside of the block.
-    Label failureRestore;
+    // depending on whether the input is a Two-Byte or a Latin-1 string.
+    CreateDependentString depStrs[] {
+        { CharEncoding::TwoByte, temp3, temp4, temp5, &oolEntry },
+        { CharEncoding::Latin1, temp3, temp4, temp5, &oolEntry },
+    };
+
     {
         Label isLatin1, done;
         masm.branchLatin1String(input, &isLatin1);
 
-        Label* failure = &oolEntry;
-        Register temp4 = (maybeTemp4 == InvalidReg) ? lastIndex : maybeTemp4;
-
-        if (maybeTemp4 == InvalidReg) {
-            failure = &failureRestore;
-
-            // Save lastIndex value to temporary space.
-            masm.store32(lastIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength()));
-        }
-
-        for (int isLatin = 0; isLatin <= 1; isLatin++) {
-            if (isLatin)
+        for (auto& depStr : depStrs) {
+            if (depStr.encoding() == CharEncoding::Latin1)
                 masm.bind(&isLatin1);
 
             Label matchLoop;
             masm.bind(&matchLoop);
 
+            static_assert(MatchPair::NoMatch == -1,
+                          "MatchPair::start is negative if no match was found");
+
             Label isUndefined, storeDone;
-            masm.branch32(Assembler::LessThan, stringIndexAddress, Imm32(0), &isUndefined);
-
-            depStr[isLatin].generate(masm, cx->names(),
-                                     CompileRuntime::get(cx->runtime()),
-                                     isLatin, temp3, input, temp4, temp5,
-                                     stringIndexAddress, stringLimitAddress,
-                                     stringsCanBeInNursery,
-                                     failure);
-
-            masm.storeValue(JSVAL_TYPE_STRING, temp3, stringAddress);
-            // Storing into nursery-allocated results object's elements; no post barrier.
-            masm.jump(&storeDone);
+            masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0), &isUndefined);
+            {
+                depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()),
+                                input, matchPairStart, matchPairLimit,
+                                stringsCanBeInNursery);
+
+                // Storing into nursery-allocated results object's elements; no post barrier.
+                masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement);
+                masm.jump(&storeDone);
+            }
             masm.bind(&isUndefined);
-
-            masm.storeValue(UndefinedValue(), stringAddress);
+            {
+                masm.storeValue(UndefinedValue(), objectMatchElement);
+            }
             masm.bind(&storeDone);
 
             masm.add32(Imm32(1), matchIndex);
             masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex, &done);
             masm.jump(&matchLoop);
         }
 
-        if (maybeTemp4 == InvalidReg) {
-            // Restore lastIndex value from temporary space, both for success
-            // and failure cases.
-
-            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex);
-            masm.jump(&done);
-
-            masm.bind(&failureRestore);
-            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex);
-
-            // Restore the match object for failure case.
-            masm.store32(Imm32(templateObject->getDenseInitializedLength()),
-                         Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
-            masm.store32(Imm32(templateObject->length()),
-                         Address(object, elementsOffset + ObjectElements::offsetOfLength()));
-            masm.jump(&oolEntry);
-        }
+#ifdef DEBUG
+        masm.assumeUnreachable("The match string loop doesn't fall through.");
+#endif
 
         masm.bind(&done);
     }
 
     // Fill in the rest of the output object.
-    masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
-    masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength()));
+    masm.store32(matchIndex,
+                 Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
+    masm.store32(matchIndex,
+                 Address(object, elementsOffset + ObjectElements::offsetOfLength()));
 
     masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
 
-    MOZ_ASSERT(templateObject->numFixedSlots() == 0);
-    MOZ_ASSERT(templateObject->lookupPure(cx->names().index)->slot() == 0);
-    MOZ_ASSERT(templateObject->lookupPure(cx->names().input)->slot() == 1);
-
-    masm.load32(pairsVectorAddress, temp3);
+    masm.load32(firstMatchPairStartAddress, temp3);
     masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
-    Address inputSlotAddress(temp2, sizeof(Value));
-    masm.storeValue(JSVAL_TYPE_STRING, input, inputSlotAddress);
-    // No post barrier needed (inputSlotAddress is within nursery object.)
+
+    // No post barrier needed (address is within nursery object.)
+    masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
 
     // All done!
     masm.tagValue(JSVAL_TYPE_OBJECT, object, result);
     masm.ret();
 
     masm.bind(&notFound);
     masm.moveValue(NullValue(), result);
     masm.ret();
 
-    // Fallback paths for CreateDependentString and createGCObject.
-    // Need to save all registers in use when they were called.
-    LiveRegisterSet regsToSave(RegisterSet::Volatile());
-    regsToSave.addUnchecked(regexp);
-    regsToSave.addUnchecked(input);
-    regsToSave.addUnchecked(lastIndex);
-    regsToSave.addUnchecked(temp1);
-    regsToSave.addUnchecked(temp2);
-    regsToSave.addUnchecked(temp3);
-    if (maybeTemp4 != InvalidReg)
-        regsToSave.addUnchecked(maybeTemp4);
-    regsToSave.addUnchecked(temp5);
-
-    for (int isLatin = 0; isLatin <= 1; isLatin++)
-        depStr[isLatin].generateFallback(masm, regsToSave);
-
+    // Fallback paths for CreateDependentString.
+    for (auto& depStr : depStrs)
+        depStr.generateFallback(masm);
+
+    // Fallback path for createGCObject.
     masm.bind(&matchResultFallback);
-    CreateMatchResultFallback(masm, regsToSave, object, temp2, temp5, templateObject, &oolEntry);
+    CreateMatchResultFallback(masm, object, temp2, temp3, templateObj, &oolEntry);
     masm.jump(&matchResultJoin);
 
     // Use an undefined value to signal to the caller that the OOL stub needs to be called.
     masm.bind(&oolEntry);
     masm.moveValue(UndefinedValue(), result);
     masm.ret();
 
     Linker linker(masm);
@@ -2307,24 +2359,54 @@ JitRealm::generateRegExpSearcherStub(JSC
     if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
                                  temp1, temp2, temp3, inputOutputDataStartOffset,
                                  RegExpShared::Normal, stringsCanBeInNursery,
                                  &notFound, &oolEntry))
     {
         return nullptr;
     }
 
+    /*
+     * [SMDOC] Stack layout for the RegExpSearcher stub
+     *
+     *                                    +---------------+
+     *                                    |Return-Address |
+     *                                    +---------------+
+     * inputOutputDataStartOffset +-----> +---------------+
+     *                                    |InputOutputData|
+     *                                    +---------------+
+     *                                    +---------------+
+     *                                    |  MatchPairs   |
+     *                                    |       count   |
+     *                                    |       pairs   |
+     *                                    |               |
+     *                                    +---------------+
+     *     pairsVectorStartOffset +-----> +---------------+
+     *                                    |   MatchPair   |
+     *             matchPairStart +------------>  start   |  <-------+
+     *             matchPairLimit +------------>  limit   |          | Reserved space for
+     *                                    +---------------+          | `RegExpObject::MaxPairCount`
+     *                                           .                   | MatchPair objects.
+     *                                           .                   |
+     *                                           .                   | Only a single object will
+     *                                    +---------------+          | be initialized and can be
+     *                                    |   MatchPair   |          | accessed below.
+     *                                    |       start   |  <-------+
+     *                                    |       limit   |
+     *                                    +---------------+
+     */
+
     size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
-    Address stringIndexAddress(masm.getStackPointer(),
-                               pairsVectorStartOffset + offsetof(MatchPair, start));
-    Address stringLimitAddress(masm.getStackPointer(),
-                               pairsVectorStartOffset + offsetof(MatchPair, limit));
-
-    masm.load32(stringIndexAddress, result);
-    masm.load32(stringLimitAddress, input);
+    Address matchPairStart(masm.getStackPointer(),
+                           pairsVectorStartOffset + offsetof(MatchPair, start));
+    Address matchPairLimit(masm.getStackPointer(),
+                           pairsVectorStartOffset + offsetof(MatchPair, limit));
+
+    masm.load32(matchPairStart, result);
+    masm.load32(matchPairLimit, input);
     masm.lshiftPtr(Imm32(15), input);
     masm.or32(input, result);
     masm.ret();
 
     masm.bind(&notFound);
     masm.move32(Imm32(RegExpSearcherResultNotFound), result);
     masm.ret();
 
@@ -2683,29 +2765,37 @@ CodeGenerator::visitOutOfLineRegExpInsta
     masm.storeCallBoolResult(output);
 
     restoreVolatile(output);
 
     masm.jump(ool->rejoin());
 }
 
 static void
-FindFirstDollarIndex(MacroAssembler& masm, Register len, Register chars,
-                     Register temp, Register output, bool isLatin1)
-{
+FindFirstDollarIndex(MacroAssembler& masm, Register str, Register len, Register temp0,
+                     Register temp1, Register output, CharEncoding encoding)
+{
+#ifdef DEBUG
+    Label ok;
+    masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
+    masm.assumeUnreachable("Length should be greater than 0.");
+    masm.bind(&ok);
+#endif
+
+    Register chars = temp0;
+    masm.loadStringChars(str, chars, encoding);
+
     masm.move32(Imm32(0), output);
 
     Label start, done;
     masm.bind(&start);
-    if (isLatin1)
-        masm.load8ZeroExtend(BaseIndex(chars, output, TimesOne), temp);
-    else
-        masm.load16ZeroExtend(BaseIndex(chars, output, TimesTwo), temp);
-
-    masm.branch32(Assembler::Equal, temp, Imm32('$'), &done);
+
+    Register currentChar = temp1;
+    masm.loadChar(chars, output, currentChar, encoding);
+    masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done);
 
     masm.add32(Imm32(1), output);
     masm.branch32(Assembler::NotEqual, output, len, &start);
 
     masm.move32(Imm32(-1), output);
 
     masm.bind(&done);
 }
@@ -2727,24 +2817,22 @@ CodeGenerator::visitGetFirstDollarIndex(
                                    StoreRegisterTo(output));
 
     masm.branchIfRope(str, ool->entry());
     masm.loadStringLength(str, len);
 
     Label isLatin1, done;
     masm.branchLatin1String(str, &isLatin1);
     {
-        masm.loadStringChars(str, temp0, CharEncoding::TwoByte);
-        FindFirstDollarIndex(masm, len, temp0, temp1, output, /* isLatin1 = */ false);
+        FindFirstDollarIndex(masm, str, len, temp0, temp1, output, CharEncoding::TwoByte);
         masm.jump(&done);
     }
     masm.bind(&isLatin1);
     {
-        masm.loadStringChars(str, temp0, CharEncoding::Latin1);
-        FindFirstDollarIndex(masm, len, temp0, temp1, output, /* isLatin1 = */ true);
+        FindFirstDollarIndex(masm, str, len, temp0, temp1, output, CharEncoding::Latin1);
     }
     masm.bind(&done);
     masm.bind(ool->rejoin());
 }
 
 typedef JSString* (*StringReplaceFn)(JSContext*, HandleString, HandleString, HandleString);
 static const VMFunction StringFlatReplaceInfo =
     FunctionInfo<StringReplaceFn>(js::str_flat_replace_string, "str_flat_replace_string");
@@ -2815,18 +2903,44 @@ CodeGenerator::emitSharedStub(ICStub::Ki
 void
 CodeGenerator::visitBinaryCache(LBinaryCache* lir)
 {
     LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
     TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(lir, LBinaryCache::LhsInput));
     TypedOrValueRegister rhs = TypedOrValueRegister(ToValue(lir, LBinaryCache::RhsInput));
     ValueOperand output = ToOutValue(lir);
 
-    IonBinaryArithIC ic(liveRegs, lhs, rhs, output);
-    addIC(lir, allocateIC(ic));
+    JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc());
+
+    switch (jsop) {
+      case JSOP_ADD:
+      case JSOP_SUB:
+      case JSOP_MUL:
+      case JSOP_DIV:
+      case JSOP_MOD:
+      case JSOP_POW: {
+        IonBinaryArithIC ic(liveRegs, lhs, rhs, output);
+        addIC(lir, allocateIC(ic));
+        return;
+      }
+      case JSOP_LT:
+      case JSOP_LE:
+      case JSOP_GT:
+      case JSOP_GE:
+      case JSOP_EQ:
+      case JSOP_NE:
+      case JSOP_STRICTEQ:
+      case JSOP_STRICTNE: {
+        IonCompareIC ic(liveRegs, lhs, rhs, output);
+        addIC(lir, allocateIC(ic));
+        return;
+      }
+      default:
+        MOZ_CRASH("Unsupported jsop in MBinaryCache");
+    }
 }
 
 void
 CodeGenerator::visitBinarySharedStub(LBinarySharedStub* lir)
 {
     JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc());
     switch (jsop) {
       case JSOP_ADD:
@@ -8292,149 +8406,144 @@ CodeGenerator::visitConcat(LConcat* lir)
     MOZ_ASSERT(ToRegister(lir->temp5()) == CallTempReg4);
     MOZ_ASSERT(output == CallTempReg5);
 
     emitConcat(lir, lhs, rhs, output);
 }
 
 static void
 CopyStringChars(MacroAssembler& masm, Register to, Register from, Register len,
-                Register byteOpScratch, size_t fromWidth, size_t toWidth)
+                Register byteOpScratch, CharEncoding fromEncoding, CharEncoding toEncoding)
 {
     // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0
     // (checked below in debug builds), and when done |to| must point to the
     // next available char.
 
 #ifdef DEBUG
     Label ok;
     masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
     masm.assumeUnreachable("Length should be greater than 0.");
     masm.bind(&ok);
 #endif
 
-    MOZ_ASSERT(fromWidth == 1 || fromWidth == 2);
-    MOZ_ASSERT(toWidth == 1 || toWidth == 2);
-    MOZ_ASSERT_IF(toWidth == 1, fromWidth == 1);
+    MOZ_ASSERT_IF(toEncoding == CharEncoding::Latin1, fromEncoding == CharEncoding::Latin1);
+
+    size_t fromWidth = fromEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t);
+    size_t toWidth = toEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t);
 
     Label start;
     masm.bind(&start);
-    if (fromWidth == 2)
-        masm.load16ZeroExtend(Address(from, 0), byteOpScratch);
-    else
-        masm.load8ZeroExtend(Address(from, 0), byteOpScratch);
-    if (toWidth == 2)
-        masm.store16(byteOpScratch, Address(to, 0));
-    else
-        masm.store8(byteOpScratch, Address(to, 0));
+    masm.loadChar(Address(from, 0), byteOpScratch, fromEncoding);
+    masm.storeChar(byteOpScratch, Address(to, 0), toEncoding);
     masm.addPtr(Imm32(fromWidth), from);
     masm.addPtr(Imm32(toWidth), to);
     masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
 }
 
 static void
+CopyStringChars(MacroAssembler& masm, Register to, Register from, Register len,
+                Register byteOpScratch, CharEncoding encoding)
+{
+    CopyStringChars(masm, to, from, len, byteOpScratch, encoding, encoding);
+}
+
+static void
 CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, Register destChars,
                             Register temp1, Register temp2)
 {
     // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may
     // have to inflate.
 
     Label isLatin1, done;
     masm.loadStringLength(input, temp1);
     masm.branchLatin1String(input, &isLatin1);
     {
         masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
         masm.movePtr(temp2, input);
-        CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char16_t), sizeof(char16_t));
+        CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::TwoByte);
         masm.jump(&done);
     }
     masm.bind(&isLatin1);
     {
         masm.loadStringChars(input, temp2, CharEncoding::Latin1);
         masm.movePtr(temp2, input);
-        CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char), sizeof(char16_t));
+        CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::Latin1,
+                        CharEncoding::TwoByte);
     }
     masm.bind(&done);
 }
 
 static void
 ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, Register output,
                    Register temp1, Register temp2, Register temp3,
                    bool stringsCanBeInNursery,
-                   Label* failure, bool isTwoByte)
+                   Label* failure, CharEncoding encoding)
 {
     JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)",
-            (isTwoByte ? "Two-Byte" : "Latin-1"));
+            (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
 
     // State: result length in temp2.
 
     // Ensure both strings are linear.
     masm.branchIfRope(lhs, failure);
     masm.branchIfRope(rhs, failure);
 
     // Allocate a JSThinInlineString or JSFatInlineString.
     size_t maxThinInlineLength;
-    if (isTwoByte)
+    if (encoding == CharEncoding::Latin1)
+        maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1;
+    else
         maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE;
-    else
-        maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1;
 
     Label isFat, allocDone;
     masm.branch32(Assembler::Above, temp2, Imm32(maxThinInlineLength), &isFat);
     {
         uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS;
-        if (!isTwoByte)
+        if (encoding == CharEncoding::Latin1)
             flags |= JSString::LATIN1_CHARS_BIT;
         masm.newGCString(output, temp1, failure, stringsCanBeInNursery);
         masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
         masm.jump(&allocDone);
     }
     masm.bind(&isFat);
     {
         uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS;
-        if (!isTwoByte)
+        if (encoding == CharEncoding::Latin1)
             flags |= JSString::LATIN1_CHARS_BIT;
         masm.newGCFatInlineString(output, temp1, failure, stringsCanBeInNursery);
         masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
     }
     masm.bind(&allocDone);
 
     // Store length.
     masm.store32(temp2, Address(output, JSString::offsetOfLength()));
 
     // Load chars pointer in temp2.
     masm.loadInlineStringCharsForStore(output, temp2);
 
-    {
-        // Copy lhs chars. Note that this advances temp2 to point to the next
-        // char. This also clobbers the lhs register.
-        if (isTwoByte) {
-            CopyStringCharsMaybeInflate(masm, lhs, temp2, temp1, temp3);
+    auto copyChars = [&](Register src) {
+        if (encoding == CharEncoding::TwoByte) {
+            CopyStringCharsMaybeInflate(masm, src, temp2, temp1, temp3);
         } else {
-            masm.loadStringLength(lhs, temp3);
-            masm.loadStringChars(lhs, temp1, CharEncoding::Latin1);
-            masm.movePtr(temp1, lhs);
-            CopyStringChars(masm, temp2, lhs, temp3, temp1, sizeof(char), sizeof(char));
-        }
-
-        // Copy rhs chars. Clobbers the rhs register.
-        if (isTwoByte) {
-            CopyStringCharsMaybeInflate(masm, rhs, temp2, temp1, temp3);
-        } else {
-            masm.loadStringLength(rhs, temp3);
-            masm.loadStringChars(rhs, temp1, CharEncoding::Latin1);
-            masm.movePtr(temp1, rhs);
-            CopyStringChars(masm, temp2, rhs, temp3, temp1, sizeof(char), sizeof(char));
-        }
-
-        // Null-terminate.
-        if (isTwoByte)
-            masm.store16(Imm32(0), Address(temp2, 0));
-        else
-            masm.store8(Imm32(0), Address(temp2, 0));
-    }
+            masm.loadStringLength(src, temp3);
+            masm.loadStringChars(src, temp1, CharEncoding::Latin1);
+            masm.movePtr(temp1, src);
+            CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1);
+        }
+    };
+
+    // Copy lhs chars. Note that this advances temp2 to point to the next
+    // char. This also clobbers the lhs register.
+    copyChars(lhs);
+
+    // Copy rhs chars. Clobbers the rhs register.
+    copyChars(rhs);
+
+    // Null-terminate.
+    masm.storeChar(Imm32(0), Address(temp2, 0), encoding);
 
     masm.ret();
 }
 
 typedef JSString* (*SubstringKernelFn)(JSContext* cx, HandleString str, int32_t begin, int32_t len);
 static const VMFunction SubstringKernelInfo =
     FunctionInfo<SubstringKernelFn>(SubstringKernel, "SubstringKernel");
 
@@ -8477,79 +8586,69 @@ CodeGenerator::visitSubstr(LSubstr* lir)
     masm.bind(&nonZero);
     masm.branchIfRopeOrExternal(string, temp, slowPath);
 
     // Handle inlined strings by creating a FatInlineString.
     masm.branchTest32(Assembler::Zero, stringFlags, Imm32(JSString::INLINE_CHARS_BIT), &notInline);
     masm.newGCFatInlineString(output, temp, slowPath, stringsCanBeInNursery());
     masm.store32(length, Address(output, JSString::offsetOfLength()));
 
-    masm.branchLatin1String(string, &isInlinedLatin1);
-    {
-        masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS),
-                     Address(output, JSString::offsetOfFlags()));
-        masm.loadInlineStringChars(string, temp, CharEncoding::TwoByte);
+    auto initializeFatInlineString = [&](CharEncoding encoding) {
+        uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS;
+        if (encoding == CharEncoding::Latin1)
+            flags |= JSString::LATIN1_CHARS_BIT;
+
+        masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
+        masm.loadInlineStringChars(string, temp, encoding);
+        masm.addToCharPtr(temp, begin, encoding);
         if (temp2 == string)
             masm.push(string);
-        BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
-        masm.computeEffectiveAddress(chars, temp2);
-        masm.loadInlineStringCharsForStore(output, temp);
-        CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char16_t), sizeof(char16_t));
-        masm.load32(Address(output, JSString::offsetOfLength()), length);
-        masm.store16(Imm32(0), Address(temp, 0));
+        masm.loadInlineStringCharsForStore(output, temp2);
+        CopyStringChars(masm, temp2, temp, length, temp3, encoding);
+        masm.loadStringLength(output, length);
+        masm.storeChar(Imm32(0), Address(temp2, 0), encoding);
         if (temp2 == string)
             masm.pop(string);
         masm.jump(done);
+    };
+
+    masm.branchLatin1String(string, &isInlinedLatin1);
+    {
+        initializeFatInlineString(CharEncoding::TwoByte);
     }
     masm.bind(&isInlinedLatin1);
     {
-        masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT),
-                     Address(output, JSString::offsetOfFlags()));
-        if (temp2 == string) {
-            masm.push(string);
-            masm.loadInlineStringChars(string, temp, CharEncoding::Latin1);
-            masm.movePtr(temp, temp2);
-        } else {
-            masm.loadInlineStringChars(string, temp2, CharEncoding::Latin1);
-        }
-        static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
-        masm.addPtr(begin, temp2);
-        masm.loadInlineStringCharsForStore(output, temp);
-        CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char), sizeof(char));
-        masm.load32(Address(output, JSString::offsetOfLength()), length);
-        masm.store8(Imm32(0), Address(temp, 0));
-        if (temp2 == string)
-            masm.pop(string);
-        masm.jump(done);
+        initializeFatInlineString(CharEncoding::Latin1);
     }
 
     // Handle other cases with a DependentString.
     masm.bind(&notInline);
     masm.newGCString(output, temp, slowPath, gen->stringsCanBeInNursery());
     masm.store32(length, Address(output, JSString::offsetOfLength()));
     masm.storeDependentStringBase(string, output);
 
+    auto initializeDependentString = [&](CharEncoding encoding) {
+        uint32_t flags = JSString::DEPENDENT_FLAGS;
+        if (encoding == CharEncoding::Latin1)
+            flags |= JSString::LATIN1_CHARS_BIT;
+
+        masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
+        masm.loadNonInlineStringChars(string, temp, encoding);
+        masm.addToCharPtr(temp, begin, encoding);
+        masm.storeNonInlineStringChars(temp, output);
+        masm.jump(done);
+    };
+
     masm.branchLatin1String(string, &isLatin1);
     {
-        masm.store32(Imm32(JSString::DEPENDENT_FLAGS), Address(output, JSString::offsetOfFlags()));
-        masm.loadNonInlineStringChars(string, temp, CharEncoding::TwoByte);
-        BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
-        masm.computeEffectiveAddress(chars, temp);
-        masm.storeNonInlineStringChars(temp, output);
-        masm.jump(done);
+        initializeDependentString(CharEncoding::TwoByte);
     }
     masm.bind(&isLatin1);
     {
-        masm.store32(Imm32(JSString::DEPENDENT_FLAGS | JSString::LATIN1_CHARS_BIT),
-                     Address(output, JSString::offsetOfFlags()));
-        masm.loadNonInlineStringChars(string, temp, CharEncoding::Latin1);
-        static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
-        masm.addPtr(begin, temp);
-        masm.storeNonInlineStringChars(temp, output);
-        masm.jump(done);
+        initializeDependentString(CharEncoding::Latin1);
     }
 
     masm.bind(done);
 }
 
 JitCode*
 JitRealm::generateStringConcatStub(JSContext* cx)
 {
@@ -8628,21 +8727,21 @@ JitRealm::generateStringConcatStub(JSCon
     masm.ret();
 
     masm.bind(&rightEmpty);
     masm.mov(lhs, output);
     masm.ret();
 
     masm.bind(&isFatInlineTwoByte);
     ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
-                       stringsCanBeInNursery, &failure, true);
+                       stringsCanBeInNursery, &failure, CharEncoding::TwoByte);
 
     masm.bind(&isFatInlineLatin1);
     ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
-                       stringsCanBeInNursery, &failure, false);
+                       stringsCanBeInNursery, &failure, CharEncoding::Latin1);
 
     masm.pop(temp2);
     masm.pop(temp1);
 
     masm.bind(&failure);
     masm.movePtr(ImmPtr(nullptr), output);
     masm.ret();
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5825,17 +5825,17 @@ IonBuilder::jsop_compare(JSOp op, MDefin
         MOZ_TRY(compareTryBitwise(&emitted, op, left, right));
         if (emitted)
             return Ok();
         MOZ_TRY(compareTrySpecializedOnBaselineInspector(&emitted, op, left, right));
         if (emitted)
             return Ok();
     }
 
-    MOZ_TRY(compareTrySharedStub(&emitted, left, right));
+    MOZ_TRY(compareTryBinaryStub(&emitted, left, right));
     if (emitted)
         return Ok();
 
     trackOptimizationAttempt(TrackedStrategy::Compare_Call);
 
     // Not possible to optimize. Do a slow vm call.
     MCompare* ins = MCompare::New(alloc(), left, right, op);
     ins->cacheOperandMightEmulateUndefined(constraints());
@@ -6020,31 +6020,29 @@ IonBuilder::compareTrySpecializedOnBasel
 
     MOZ_ASSERT(!ins->isEffectful());
     trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
-IonBuilder::compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right)
+IonBuilder::compareTryBinaryStub(bool* emitted, MDefinition* left, MDefinition* right)
 {
     MOZ_ASSERT(*emitted == false);
 
     // Try to emit a shared stub cache.
 
     if (JitOptions.disableSharedStubs)
         return Ok();
 
     if (JSOp(*pc) == JSOP_CASE)
         return Ok();
 
-    trackOptimizationAttempt(TrackedStrategy::Compare_SharedCache);
-
-    MBinarySharedStub* stub = MBinarySharedStub::New(alloc(), left, right);
+    MBinaryCache* stub = MBinaryCache::New(alloc(), left, right);
     current->add(stub);
     current->push(stub);
     MOZ_TRY(resumeAfter(stub));
 
     MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Boolean, MUnbox::Infallible);
     current->add(unbox);
     current->push(unbox);
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -327,17 +327,17 @@ class IonBuilder
     // jsop_compare helpers.
     AbortReasonOr<Ok> compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
                                             MDefinition* right, bool canTrackOptimization);
     AbortReasonOr<Ok> compareTryBitwise(bool* emitted, JSOp op, MDefinition* left,
                                         MDefinition* right);
     AbortReasonOr<Ok> compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
                                                                MDefinition* left,
                                                                MDefinition* right);
-    AbortReasonOr<Ok> compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right);
+    AbortReasonOr<Ok> compareTryBinaryStub(bool* emitted, MDefinition* left, MDefinition* right);
 
     // jsop_newarray helpers.
     AbortReasonOr<Ok> newArrayTryTemplateObject(bool* emitted, JSObject* templateObject,
                                                 uint32_t length);
     AbortReasonOr<Ok> newArrayTryVM(bool* emitted, JSObject* templateObject, uint32_t length);
 
     // jsop_newobject helpers.
     AbortReasonOr<Ok> newObjectTrySharedStub(bool* emitted);
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -532,18 +532,31 @@ IonCacheIRCompiler::init()
         liveRegs_.emplace(ic->liveRegs());
         outputUnchecked_.emplace(TypedOrValueRegister(output));
 
         MOZ_ASSERT(numInputs == 2);
         allocator.initInputLocation(0, ic->lhs());
         allocator.initInputLocation(1, ic->rhs());
         break;
       }
+      case CacheKind::Compare: {
+        IonCompareIC *ic = ic_->asCompareIC();
+        ValueOperand output = ic->output();
+
+        available.add(output);
+
+        liveRegs_.emplace(ic->liveRegs());
+        outputUnchecked_.emplace(TypedOrValueRegister(output));
+
+        MOZ_ASSERT(numInputs == 2);
+        allocator.initInputLocation(0, ic->lhs());
+        allocator.initInputLocation(1, ic->rhs());
+        break;
+      }
       case CacheKind::Call:
-      case CacheKind::Compare:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
         MOZ_CRASH("Unsupported IC");
     }
 
     if (liveRegs_)
         liveFloatRegs_ = LiveFloatRegisterSet(liveRegs_->fpus());
@@ -574,18 +587,16 @@ IonCacheIRCompiler::compile()
 
           default:
             MOZ_CRASH("Invalid op");
         }
 
         allocator.nextOp();
     } while (reader.more());
 
-    MOZ_RELEASE_ASSERT(nextStubField_ == writer_.numStubFields());
-
     masm.assumeUnreachable("Should have returned from IC");
 
     // Done emitting the main IC code. Now emit the failure paths.
     for (size_t i = 0; i < failurePaths.length(); i++) {
         if (!emitFailurePath(i))
             return nullptr;
         Register scratch = ic_->scratchRegisterForEntryJump();
         CodeOffset offset = masm.movWithPatch(ImmWord(-1), scratch);
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -55,18 +55,19 @@ IonIC::scratchRegisterForEntryJump()
       case CacheKind::GetIterator:
         return asGetIteratorIC()->temp1();
       case CacheKind::InstanceOf:
         return asInstanceOfIC()->output();
       case CacheKind::UnaryArith:
         return asUnaryArithIC()->output().scratchReg();
       case CacheKind::BinaryArith:
         return asBinaryArithIC()->output().scratchReg();
+      case CacheKind::Compare:
+        return asCompareIC()->output().scratchReg();
       case CacheKind::Call:
-      case CacheKind::Compare:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
         MOZ_CRASH("Unsupported IC");
     }
 
     MOZ_CRASH("Invalid kind");
 }
@@ -624,16 +625,93 @@ IonBinaryArithIC::update(JSContext* cx, 
 
             if (!attached)
                 ic->state().trackNotAttached();
         }
     }
     return true;
 }
 
+/* static */ bool
+IonCompareIC::update(JSContext* cx, HandleScript outerScript, IonCompareIC* ic,
+                                    HandleValue lhs, HandleValue rhs, MutableHandleValue res)
+{
+    IonScript* ionScript = outerScript->ionScript();
+    RootedScript script(cx, ic->script());
+    jsbytecode* pc = ic->pc();
+    JSOp op = JSOp(*pc);
+
+    // Case operations in a CONDSWITCH are performing strict equality.
+    if (op == JSOP_CASE)
+        op = JSOP_STRICTEQ;
+
+    // Don't pass lhs/rhs directly, we need the original values when
+    // generating stubs.
+    RootedValue lhsCopy(cx, lhs);
+    RootedValue rhsCopy(cx, rhs);
+
+    // Perform the compare operation.
+    bool out;
+    switch (op) {
+      case JSOP_LT:
+        if (!LessThan(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_LE:
+        if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_GT:
+        if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_GE:
+        if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_EQ:
+        if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_NE:
+        if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_STRICTEQ:
+        if (!StrictlyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      case JSOP_STRICTNE:
+        if (!StrictlyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
+            return false;
+        break;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unhandled ion compare op");
+        return false;
+    }
+
+    res.setBoolean(out);
+
+    if (ic->state().maybeTransition())
+        ic->discardStubs(cx->zone());
+
+    if (ic->state().canAttachStub()) {
+        bool attached = false;
+        CompareIRGenerator gen(cx, script, pc, ic->state().mode(),
+                               op, lhs, rhs);
+        if (gen.tryAttachStub()) {
+            ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
+
+            if (!attached)
+                ic->state().trackNotAttached();
+        }
+    }
+    return true;
+}
+
 uint8_t*
 IonICStub::stubDataStart()
 {
     return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
 }
 
 void
 IonIC::attachStub(IonICStub* newStub, JitCode* code)
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -60,16 +60,17 @@ class IonGetPropertyIC;
 class IonSetPropertyIC;
 class IonGetPropSuperIC;
 class IonGetNameIC;
 class IonBindNameIC;
 class IonGetIteratorIC;
 class IonHasOwnIC;
 class IonInIC;
 class IonInstanceOfIC;
+class IonCompareIC;
 class IonUnaryArithIC;
 class IonBinaryArithIC;
 
 class IonIC
 {
     // This either points at the OOL path for the fallback path, or the code for
     // the first stub.
     uint8_t* codeRaw_;
@@ -169,16 +170,20 @@ class IonIC
     IonInIC* asInIC() {
         MOZ_ASSERT(kind_ == CacheKind::In);
         return (IonInIC*)this;
     }
     IonInstanceOfIC* asInstanceOfIC() {
         MOZ_ASSERT(kind_ == CacheKind::InstanceOf);
         return (IonInstanceOfIC*)this;
     }
+    IonCompareIC* asCompareIC() {
+        MOZ_ASSERT(kind_ == CacheKind::Compare);
+        return (IonCompareIC*)this;
+    }
     IonUnaryArithIC* asUnaryArithIC() {
         MOZ_ASSERT(kind_ == CacheKind::UnaryArith);
         return (IonUnaryArithIC*)this;
     }
     IonBinaryArithIC* asBinaryArithIC() {
         MOZ_ASSERT(kind_ == CacheKind::BinaryArith);
         return (IonBinaryArithIC*)this;
     }
@@ -480,16 +485,43 @@ class IonInstanceOfIC : public IonIC
     Register rhs() const { return rhs_; }
     Register output() const { return output_; }
 
     // This signature mimics that of TryAttachInstanceOfStub in baseline
     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonInstanceOfIC* ic,
                                     HandleValue lhs, HandleObject rhs, bool* attached);
 };
 
+class IonCompareIC : public IonIC
+{
+    LiveRegisterSet liveRegs_;
+
+    TypedOrValueRegister lhs_;
+    TypedOrValueRegister rhs_;
+    ValueOperand output_;
+
+    public:
+
+    IonCompareIC(LiveRegisterSet liveRegs, TypedOrValueRegister lhs, TypedOrValueRegister rhs,  ValueOperand output)
+      : IonIC(CacheKind::Compare),
+        liveRegs_(liveRegs),
+        lhs_(lhs),
+        rhs_(rhs),
+        output_(output)
+    { }
+
+    LiveRegisterSet liveRegs() const { return liveRegs_; }
+    TypedOrValueRegister lhs() const { return lhs_; }
+    TypedOrValueRegister rhs() const { return rhs_; }
+    ValueOperand output() const { return output_; }
+
+    static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonCompareIC* stub,
+                                    HandleValue lhs, HandleValue rhs, MutableHandleValue res);
+};
+
 class IonUnaryArithIC : public IonIC
 {
     LiveRegisterSet liveRegs_;
 
     TypedOrValueRegister input_;
     ValueOperand output_;
 
     public:
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1598,26 +1598,16 @@ MacroAssembler::loadDependentStringBase(
                       Address(str, JSString::offsetOfFlags()), Imm32(JSString::HAS_BASE_BIT),
                       dest, str);
     }
 
     loadPtr(Address(str, JSDependentString::offsetOfBase()), dest);
 }
 
 void
-MacroAssembler::leaNewDependentStringBase(Register str, Register dest)
-{
-    MOZ_ASSERT(str != dest);
-
-    // Spectre-safe because this is a newly allocated dependent string, thus we
-    // are certain of its type and the type of its base field.
-    computeEffectiveAddress(Address(str, JSDependentString::offsetOfBase()), dest);
-}
-
-void
 MacroAssembler::storeDependentStringBase(Register base, Register str)
 {
     storePtr(base, Address(str, JSDependentString::offsetOfBase()));
 }
 
 void
 MacroAssembler::loadStringChar(Register str, Register index, Register output, Register scratch,
                                Label* fail)
@@ -1644,22 +1634,22 @@ MacroAssembler::loadStringChar(Register 
 
     bind(&notRope);
 
     Label isLatin1, done;
     // We have to check the left/right side for ropes,
     // because a TwoByte rope might have a Latin1 child.
     branchLatin1String(output, &isLatin1);
     loadStringChars(output, scratch, CharEncoding::TwoByte);
-    load16ZeroExtend(BaseIndex(scratch, index, TimesTwo), output);
+    loadChar(scratch, index, output, CharEncoding::TwoByte);
     jump(&done);
 
     bind(&isLatin1);
     loadStringChars(output, scratch, CharEncoding::Latin1);
-    load8ZeroExtend(BaseIndex(scratch, index, TimesOne), output);
+    loadChar(scratch, index, output, CharEncoding::Latin1);
 
     bind(&done);
 }
 
 void
 MacroAssembler::loadStringIndexValue(Register str, Register dest, Label* fail)
 {
     MOZ_ASSERT(str != dest);
@@ -1669,16 +1659,37 @@ MacroAssembler::loadStringIndexValue(Reg
     // Does not have a cached index value.
     branchTest32(Assembler::Zero, dest, Imm32(JSString::INDEX_VALUE_BIT), fail);
 
     // Extract the index.
     rshift32(Imm32(JSString::INDEX_VALUE_SHIFT), dest);
 }
 
 void
+MacroAssembler::loadChar(Register chars, Register index, Register dest, CharEncoding encoding,
+                         int32_t offset/* = 0 */)
+{
+    if (encoding == CharEncoding::Latin1)
+        loadChar(BaseIndex(chars, index, TimesOne, offset), dest, encoding);
+    else
+        loadChar(BaseIndex(chars, index, TimesTwo, offset), dest, encoding);
+}
+
+void
+MacroAssembler::addToCharPtr(Register chars, Register index, CharEncoding encoding)
+{
+    if (encoding == CharEncoding::Latin1) {
+        static_assert(sizeof(char) == 1, "Latin-1 string index shouldn't need scaling");
+        addPtr(index, chars);
+    } else {
+        computeEffectiveAddress(BaseIndex(chars, index, TimesTwo), chars);
+    }
+}
+
+void
 MacroAssembler::typeOfObject(Register obj, Register scratch, Label* slow,
                              Label* isObject, Label* isCallable, Label* isUndefined)
 {
     loadObjClassUnsafe(obj, scratch);
 
     // Proxies can emulate undefined and have complex isCallable behavior.
     branchTestClassIsProxy(true, scratch, slow);
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -2116,20 +2116,53 @@ class MacroAssembler : public MacroAssem
     void loadStringChar(Register str, Register index, Register output, Register scratch,
                         Label* fail);
 
     void loadRopeLeftChild(Register str, Register dest);
     void storeRopeChildren(Register left, Register right, Register str);
 
     void loadDependentStringBase(Register str, Register dest);
     void storeDependentStringBase(Register base, Register str);
-    void leaNewDependentStringBase(Register str, Register dest);
 
     void loadStringIndexValue(Register str, Register dest, Label* fail);
 
+    /**
+     * Store the character in |src| to |dest|.
+     */
+    template <typename T>
+    void storeChar(const T& src, Address dest, CharEncoding encoding) {
+        if (encoding == CharEncoding::Latin1)
+            store8(src, dest);
+        else
+            store16(src, dest);
+    }
+
+    /**
+     * Load the character at |src| into |dest|.
+     */
+    template <typename T>
+    void loadChar(const T& src, Register dest, CharEncoding encoding) {
+        if (encoding == CharEncoding::Latin1)
+            load8ZeroExtend(src, dest);
+        else
+            load16ZeroExtend(src, dest);
+    }
+
+    /**
+     * Load the character at |chars[index + offset]| into |dest|. The optional
+     * offset argument is not scaled to the character encoding.
+     */
+    void loadChar(Register chars, Register index, Register dest, CharEncoding encoding,
+                  int32_t offset = 0);
+
+    /**
+     * Add |index| to |chars| so that |chars| now points at |chars[index]|.
+     */
+    void addToCharPtr(Register chars, Register index, CharEncoding encoding);
+
     void loadJSContext(Register dest);
 
     void switchToRealm(Register realm);
     void switchToRealm(const void* realm, Register scratch);
     void switchToObjectRealm(Register obj, Register scratch);
     void switchToBaselineFrameRealm(Register scratch);
     void switchToWasmTlsRealm(Register scratch1, Register scratch2);
     void debugAssertContextRealm(const void* realm, Register scratch);
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -666,552 +666,16 @@ SharedStubInfo::outerScript(JSContext* c
         MOZ_ASSERT(it.isIonJS());
         outerScript_ = it.script();
         MOZ_ASSERT(!it.ionScript()->invalidated());
     }
     return outerScript_;
 }
 
 //
-// Compare_Fallback
-//
-
-static bool
-DoCompareFallback(JSContext* cx, void* payload, ICCompare_Fallback* stub_, HandleValue lhs,
-                  HandleValue rhs, MutableHandleValue ret)
-{
-    SharedStubInfo info(cx, payload, stub_->icEntry());
-    ICStubCompiler::Engine engine = info.engine();
-
-    // This fallback stub may trigger debug mode toggling.
-    DebugModeOSRVolatileStub<ICCompare_Fallback*> stub(engine, info.maybeFrame(), stub_);
-
-    jsbytecode* pc = info.pc();
-    JSOp op = JSOp(*pc);
-
-    FallbackICSpew(cx, stub, "Compare(%s)", CodeName[op]);
-
-    // Case operations in a CONDSWITCH are performing strict equality.
-    if (op == JSOP_CASE)
-        op = JSOP_STRICTEQ;
-
-    // Don't pass lhs/rhs directly, we need the original values when
-    // generating stubs.
-    RootedValue lhsCopy(cx, lhs);
-    RootedValue rhsCopy(cx, rhs);
-
-    // Perform the compare operation.
-    bool out;
-    switch (op) {
-      case JSOP_LT:
-        if (!LessThan(cx, &lhsCopy, &rhsCopy, &out))
-            return false;
-        break;
-      case JSOP_LE:
-        if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
-            return false;
-        break;
-      case JSOP_GT:
-        if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out))
-            return false;
-        break;
-      case JSOP_GE:
-        if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
-            return false;
-        break;
-      case JSOP_EQ:
-        if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
-            return false;
-        break;
-      case JSOP_NE:
-        if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
-            return false;
-        break;
-      case JSOP_STRICTEQ:
-        if (!StrictlyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
-            return false;
-        break;
-      case JSOP_STRICTNE:
-        if (!StrictlyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
-            return false;
-        break;
-      default:
-        MOZ_ASSERT_UNREACHABLE("Unhandled baseline compare op");
-        return false;
-    }
-
-    ret.setBoolean(out);
-
-    // Check if debug mode toggling made the stub invalid.
-    if (stub.invalid())
-        return true;
-
-    // Check to see if a new stub should be generated.
-    if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) {
-        // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
-        // But for now we just bail.
-        return true;
-    }
-
-    if (engine ==  ICStubEngine::Baseline && !JitOptions.disableCacheIR) {
-        RootedScript script(cx, info.outerScript(cx));
-        CompareIRGenerator gen(cx, script, pc, stub->state().mode(), op, lhs, rhs);
-        bool attached = false;
-        if (gen.tryAttachStub()) {
-            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
-                                                        BaselineCacheIRStubKind::Regular,
-                                                        engine, script, stub, &attached);
-            if (newStub)
-                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
-            return true;
-        }
-    }
-
-    // Try to generate new stubs.
-    if (lhs.isInt32() && rhs.isInt32()) {
-        JitSpew(JitSpew_BaselineIC, "  Generating %s(Int32, Int32) stub", CodeName[op]);
-        ICCompare_Int32::Compiler compiler(cx, op, engine);
-        ICStub* int32Stub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-        if (!int32Stub)
-            return false;
-
-        stub->addNewStub(int32Stub);
-        return true;
-    }
-
-    if (!cx->runtime()->jitSupportsFloatingPoint && (lhs.isNumber() || rhs.isNumber()))
-        return true;
-
-    if (lhs.isNumber() && rhs.isNumber()) {
-        JitSpew(JitSpew_BaselineIC, "  Generating %s(Number, Number) stub", CodeName[op]);
-
-        // Unlink int32 stubs, it's faster to always use the double stub.
-        stub->unlinkStubsWithKind(cx, ICStub::Compare_Int32);
-
-        ICCompare_Double::Compiler compiler(cx, op, engine);
-        ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-        if (!doubleStub)
-            return false;
-
-        stub->addNewStub(doubleStub);
-        return true;
-    }
-
-    if ((lhs.isNumber() && rhs.isUndefined()) ||
-        (lhs.isUndefined() && rhs.isNumber()))
-    {
-        JitSpew(JitSpew_BaselineIC, "  Generating %s(%s, %s) stub", CodeName[op],
-                    rhs.isUndefined() ? "Number" : "Undefined",
-                    rhs.isUndefined() ? "Undefined" : "Number");
-        ICCompare_NumberWithUndefined::Compiler compiler(cx, op, engine, lhs.isUndefined());
-        ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-        if (!doubleStub)
-            return false;
-
-        stub->addNewStub(doubleStub);
-        return true;
-    }
-
-    if (lhs.isBoolean() && rhs.isBoolean()) {
-        JitSpew(JitSpew_BaselineIC, "  Generating %s(Boolean, Boolean) stub", CodeName[op]);
-        ICCompare_Boolean::Compiler compiler(cx, op, engine);
-        ICStub* booleanStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-        if (!booleanStub)
-            return false;
-
-        stub->addNewStub(booleanStub);
-        return true;
-    }
-
-    if ((lhs.isBoolean() && rhs.isInt32()) || (lhs.isInt32() && rhs.isBoolean())) {
-        JitSpew(JitSpew_BaselineIC, "  Generating %s(%s, %s) stub", CodeName[op],
-                    rhs.isInt32() ? "Boolean" : "Int32",
-                    rhs.isInt32() ? "Int32" : "Boolean");
-        ICCompare_Int32WithBoolean::Compiler compiler(cx, op, engine, lhs.isInt32());
-        ICStub* optStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-        if (!optStub)
-            return false;
-
-        stub->addNewStub(optStub);
-        return true;
-    }
-
-    if (IsEqualityOp(op)) {
-        if (lhs.isString() && rhs.isString() && !stub->hasStub(ICStub::Compare_String)) {
-            JitSpew(JitSpew_BaselineIC, "  Generating %s(String, String) stub", CodeName[op]);
-            ICCompare_String::Compiler compiler(cx, op, engine);
-            ICStub* stringStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-            if (!stringStub)
-                return false;
-
-            stub->addNewStub(stringStub);
-            return true;
-        }
-
-        if (lhs.isSymbol() && rhs.isSymbol() && !stub->hasStub(ICStub::Compare_Symbol)) {
-            JitSpew(JitSpew_BaselineIC, "  Generating %s(Symbol, Symbol) stub", CodeName[op]);
-            ICCompare_Symbol::Compiler compiler(cx, op, engine);
-            ICStub* symbolStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-            if (!symbolStub)
-                return false;
-
-            stub->addNewStub(symbolStub);
-            return true;
-        }
-
-        if (lhs.isObject() && rhs.isObject()) {
-            MOZ_ASSERT(!stub->hasStub(ICStub::Compare_Object));
-            JitSpew(JitSpew_BaselineIC, "  Generating %s(Object, Object) stub", CodeName[op]);
-            ICCompare_Object::Compiler compiler(cx, op, engine);
-            ICStub* objectStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-            if (!objectStub)
-                return false;
-
-            stub->addNewStub(objectStub);
-            return true;
-        }
-
-        if (lhs.isNullOrUndefined() || rhs.isNullOrUndefined()) {
-            JitSpew(JitSpew_BaselineIC, "  Generating %s(Null/Undef or X, Null/Undef or X) stub",
-                    CodeName[op]);
-            bool lhsIsUndefined = lhs.isNullOrUndefined();
-            bool compareWithNull = lhs.isNull() || rhs.isNull();
-            ICCompare_ObjectWithUndefined::Compiler compiler(cx, op, engine,
-                                                             lhsIsUndefined, compareWithNull);
-            ICStub* objectStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-            if (!objectStub)
-                return false;
-
-            stub->addNewStub(objectStub);
-            return true;
-        }
-    }
-
-    stub->noteUnoptimizableAccess();
-
-    return true;
-}
-
-typedef bool (*DoCompareFallbackFn)(JSContext*, void*, ICCompare_Fallback*,
-                                    HandleValue, HandleValue, MutableHandleValue);
-static const VMFunction DoCompareFallbackInfo =
-    FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, "DoCompareFallback", TailCall,
-                                      PopValues(2));
-
-bool
-ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(R0 == JSReturnOperand);
-
-    // Restore the tail call register.
-    EmitRestoreTailCallReg(masm);
-
-    // Ensure stack is fully synced for the expression decompiler.
-    masm.pushValue(R0);
-    masm.pushValue(R1);
-
-    // Push arguments.
-    masm.pushValue(R1);
-    masm.pushValue(R0);
-    masm.push(ICStubReg);
-    pushStubPayload(masm, R0.scratchReg());
-    return tailCallVM(DoCompareFallbackInfo, masm);
-}
-
-//
-// Compare_String
-//
-
-bool
-ICCompare_String::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure, restore;
-    masm.branchTestString(Assembler::NotEqual, R0, &failure);
-    masm.branchTestString(Assembler::NotEqual, R1, &failure);
-
-    MOZ_ASSERT(IsEqualityOp(op));
-
-    // left/right are part of R0/R1. Restore R0 and R1 in the failure case.
-    Register left = R0.scratchReg();
-    Register right = R1.scratchReg();
-    masm.unboxString(R0, left);
-    masm.unboxString(R1, right);
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
-    Register scratchReg = regs.takeAny();
-
-    masm.compareStrings(op, left, right, scratchReg, &restore);
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, scratchReg, R0);
-    EmitReturnFromIC(masm);
-
-    masm.bind(&restore);
-    masm.tagValue(JSVAL_TYPE_STRING, left, R0);
-    masm.tagValue(JSVAL_TYPE_STRING, right, R1);
-
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-//
-// Compare_Symbol
-//
-
-bool
-ICCompare_Symbol::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestSymbol(Assembler::NotEqual, R0, &failure);
-    masm.branchTestSymbol(Assembler::NotEqual, R1, &failure);
-
-    MOZ_ASSERT(IsEqualityOp(op));
-
-    Register left = masm.extractSymbol(R0, ExtractTemp0);
-    Register right = masm.extractSymbol(R1, ExtractTemp1);
-
-    Label ifTrue;
-    masm.branchPtr(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue);
-
-    masm.moveValue(BooleanValue(false), R0);
-    EmitReturnFromIC(masm);
-
-    masm.bind(&ifTrue);
-    masm.moveValue(BooleanValue(true), R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-//
-// Compare_Boolean
-//
-
-bool
-ICCompare_Boolean::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestBoolean(Assembler::NotEqual, R0, &failure);
-    masm.branchTestBoolean(Assembler::NotEqual, R1, &failure);
-
-    Register left = masm.extractInt32(R0, ExtractTemp0);
-    Register right = masm.extractInt32(R1, ExtractTemp1);
-
-    // Compare payload regs of R0 and R1.
-    Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
-    masm.cmp32Set(cond, left, right, left);
-
-    // Box the result and return
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, left, R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-//
-// Compare_NumberWithUndefined
-//
-
-bool
-ICCompare_NumberWithUndefined::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    ValueOperand numberOperand, undefinedOperand;
-    if (lhsIsUndefined) {
-        numberOperand = R1;
-        undefinedOperand = R0;
-    } else {
-        numberOperand = R0;
-        undefinedOperand = R1;
-    }
-
-    Label failure;
-    masm.branchTestNumber(Assembler::NotEqual, numberOperand, &failure);
-    masm.branchTestUndefined(Assembler::NotEqual, undefinedOperand, &failure);
-
-    // Comparing a number with undefined will always be true for NE/STRICTNE,
-    // and always be false for other compare ops.
-    masm.moveValue(BooleanValue(op == JSOP_NE || op == JSOP_STRICTNE), R0);
-
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-//
-// Compare_Object
-//
-
-bool
-ICCompare_Object::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-    masm.branchTestObject(Assembler::NotEqual, R1, &failure);
-
-    MOZ_ASSERT(IsEqualityOp(op));
-
-    Register left = masm.extractObject(R0, ExtractTemp0);
-    Register right = masm.extractObject(R1, ExtractTemp1);
-
-    Label ifTrue;
-    masm.branchPtr(JSOpToCondition(op, /* signed = */true), left, right, &ifTrue);
-
-    masm.moveValue(BooleanValue(false), R0);
-    EmitReturnFromIC(masm);
-
-    masm.bind(&ifTrue);
-    masm.moveValue(BooleanValue(true), R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-//
-// Compare_ObjectWithUndefined
-//
-
-bool
-ICCompare_ObjectWithUndefined::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(IsEqualityOp(op));
-
-    ValueOperand objectOperand, undefinedOperand;
-    if (lhsIsUndefined) {
-        objectOperand = R1;
-        undefinedOperand = R0;
-    } else {
-        objectOperand = R0;
-        undefinedOperand = R1;
-    }
-
-    Label failure;
-    if (compareWithNull)
-        masm.branchTestNull(Assembler::NotEqual, undefinedOperand, &failure);
-    else
-        masm.branchTestUndefined(Assembler::NotEqual, undefinedOperand, &failure);
-
-    Label notObject;
-    masm.branchTestObject(Assembler::NotEqual, objectOperand, &notObject);
-
-    if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) {
-        // obj !== undefined for all objects.
-        masm.moveValue(BooleanValue(op == JSOP_STRICTNE), R0);
-        EmitReturnFromIC(masm);
-    } else {
-        // obj != undefined only where !obj->getClass()->emulatesUndefined()
-        Register obj = masm.extractObject(objectOperand, ExtractTemp0);
-
-        // We need a scratch register.
-        masm.push(obj);
-        Label slow, emulatesUndefined;
-        masm.branchIfObjectEmulatesUndefined(obj, obj, &slow, &emulatesUndefined);
-
-        masm.pop(obj);
-        masm.moveValue(BooleanValue(op == JSOP_NE), R0);
-        EmitReturnFromIC(masm);
-
-        masm.bind(&emulatesUndefined);
-        masm.pop(obj);
-        masm.moveValue(BooleanValue(op == JSOP_EQ), R0);
-        EmitReturnFromIC(masm);
-
-        masm.bind(&slow);
-        masm.pop(obj);
-        masm.jump(&failure);
-    }
-
-    masm.bind(&notObject);
-
-    // Also support null == null or undefined == undefined comparisons.
-    Label differentTypes;
-    if (compareWithNull)
-        masm.branchTestNull(Assembler::NotEqual, objectOperand, &differentTypes);
-    else
-        masm.branchTestUndefined(Assembler::NotEqual, objectOperand, &differentTypes);
-
-    masm.moveValue(BooleanValue(op == JSOP_STRICTEQ || op == JSOP_EQ), R0);
-    EmitReturnFromIC(masm);
-
-    masm.bind(&differentTypes);
-    // Also support null == undefined or undefined == null.
-    Label neverEqual;
-    if (compareWithNull)
-        masm.branchTestUndefined(Assembler::NotEqual, objectOperand, &neverEqual);
-    else
-        masm.branchTestNull(Assembler::NotEqual, objectOperand, &neverEqual);
-
-    masm.moveValue(BooleanValue(op == JSOP_EQ || op == JSOP_STRICTNE), R0);
-    EmitReturnFromIC(masm);
-
-    // null/undefined can only be equal to null/undefined or emulatesUndefined.
-    masm.bind(&neverEqual);
-    masm.moveValue(BooleanValue(op == JSOP_NE || op == JSOP_STRICTNE), R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-//
-// Compare_Int32WithBoolean
-//
-
-bool
-ICCompare_Int32WithBoolean::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    ValueOperand int32Val;
-    ValueOperand boolVal;
-    if (lhsIsInt32_) {
-        int32Val = R0;
-        boolVal = R1;
-    } else {
-        boolVal = R0;
-        int32Val = R1;
-    }
-    masm.branchTestBoolean(Assembler::NotEqual, boolVal, &failure);
-    masm.branchTestInt32(Assembler::NotEqual, int32Val, &failure);
-
-    if (op_ == JSOP_STRICTEQ || op_ == JSOP_STRICTNE) {
-        // Ints and booleans are never strictly equal, always strictly not equal.
-        masm.moveValue(BooleanValue(op_ == JSOP_STRICTNE), R0);
-        EmitReturnFromIC(masm);
-    } else {
-        Register boolReg = masm.extractBoolean(boolVal, ExtractTemp0);
-        Register int32Reg = masm.extractInt32(int32Val, ExtractTemp1);
-
-        // Compare payload regs of R0 and R1.
-        Assembler::Condition cond = JSOpToCondition(op_, /* signed = */true);
-        masm.cmp32Set(cond, (lhsIsInt32_ ? int32Reg : boolReg),
-                      (lhsIsInt32_ ? boolReg : int32Reg), R0.scratchReg());
-
-        // Box the result and return
-        masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.scratchReg(), R0);
-        EmitReturnFromIC(masm);
-    }
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 void
 LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result)
 {
     switch (layout) {
       case Layout_TypedArray:
         masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), result);
         break;
       case Layout_OutlineTypedObject:
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -1679,279 +1679,16 @@ class ICCompare_Fallback : public ICFall
           : ICStubCompiler(cx, ICStub::Compare_Fallback, engine) {}
 
         ICStub* getStub(ICStubSpace* space) override {
             return newStub<ICCompare_Fallback>(space, getStubCode());
         }
     };
 };
 
-class ICCompare_Int32 : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICCompare_Int32(JitCode* stubCode)
-      : ICStub(ICStub::Compare_Int32, stubCode) {}
-
-  public:
-    // Compiler for this stub kind.
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine)
-          : ICMultiStubCompiler(cx, ICStub::Compare_Int32, op, engine) {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICCompare_Int32>(space, getStubCode());
-        }
-    };
-};
-
-class ICCompare_Double : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICCompare_Double(JitCode* stubCode)
-      : ICStub(ICStub::Compare_Double, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine)
-          : ICMultiStubCompiler(cx, ICStub::Compare_Double, op, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICCompare_Double>(space, getStubCode());
-        }
-    };
-};
-
-class ICCompare_NumberWithUndefined : public ICStub
-{
-    friend class ICStubSpace;
-
-    ICCompare_NumberWithUndefined(JitCode* stubCode, bool lhsIsUndefined)
-      : ICStub(ICStub::Compare_NumberWithUndefined, stubCode)
-    {
-        extra_ = lhsIsUndefined;
-    }
-
-  public:
-    bool lhsIsUndefined() {
-        return extra_;
-    }
-
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-        bool lhsIsUndefined;
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined)
-          : ICMultiStubCompiler(cx, ICStub::Compare_NumberWithUndefined, op, engine),
-            lhsIsUndefined(lhsIsUndefined)
-        {}
-
-        virtual int32_t getKey() const override {
-            return static_cast<int32_t>(engine_) |
-                  (static_cast<int32_t>(kind) << 1) |
-                  (static_cast<int32_t>(op) << 17) |
-                  (static_cast<int32_t>(lhsIsUndefined) << 25);
-        }
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICCompare_NumberWithUndefined>(space, getStubCode(),
-                                                              lhsIsUndefined);
-        }
-    };
-};
-
-class ICCompare_String : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICCompare_String(JitCode* stubCode)
-      : ICStub(ICStub::Compare_String, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine)
-          : ICMultiStubCompiler(cx, ICStub::Compare_String, op, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICCompare_String>(space, getStubCode());
-        }
-    };
-};
-
-class ICCompare_Symbol : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICCompare_Symbol(JitCode* stubCode)
-      : ICStub(ICStub::Compare_Symbol, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine)
-          : ICMultiStubCompiler(cx, ICStub::Compare_Symbol, op, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICCompare_Symbol>(space, getStubCode());
-        }
-    };
-};
-
-class ICCompare_Boolean : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICCompare_Boolean(JitCode* stubCode)
-      : ICStub(ICStub::Compare_Boolean, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine)
-          : ICMultiStubCompiler(cx, ICStub::Compare_Boolean, op, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICCompare_Boolean>(space, getStubCode());
-        }
-    };
-};
-
-class ICCompare_Object : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICCompare_Object(JitCode* stubCode)
-      : ICStub(ICStub::Compare_Object, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine)
-          : ICMultiStubCompiler(cx, ICStub::Compare_Object, op, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICCompare_Object>(space, getStubCode());
-        }
-    };
-};
-
-class ICCompare_ObjectWithUndefined : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICCompare_ObjectWithUndefined(JitCode* stubCode)
-      : ICStub(ICStub::Compare_ObjectWithUndefined, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-        bool lhsIsUndefined;
-        bool compareWithNull;
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined, bool compareWithNull)
-          : ICMultiStubCompiler(cx, ICStub::Compare_ObjectWithUndefined, op, engine),
-            lhsIsUndefined(lhsIsUndefined),
-            compareWithNull(compareWithNull)
-        {}
-
-        virtual int32_t getKey() const override {
-            return static_cast<int32_t>(engine_) |
-                  (static_cast<int32_t>(kind) << 1) |
-                  (static_cast<int32_t>(op) << 17) |
-                  (static_cast<int32_t>(lhsIsUndefined) << 25) |
-                  (static_cast<int32_t>(compareWithNull) << 26);
-        }
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICCompare_ObjectWithUndefined>(space, getStubCode());
-        }
-    };
-};
-
-class ICCompare_Int32WithBoolean : public ICStub
-{
-    friend class ICStubSpace;
-
-    ICCompare_Int32WithBoolean(JitCode* stubCode, bool lhsIsInt32)
-      : ICStub(ICStub::Compare_Int32WithBoolean, stubCode)
-    {
-        extra_ = lhsIsInt32;
-    }
-
-  public:
-    bool lhsIsInt32() const {
-        return extra_;
-    }
-
-    // Compiler for this stub kind.
-    class Compiler : public ICStubCompiler {
-      protected:
-        JSOp op_;
-        bool lhsIsInt32_;
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-        virtual int32_t getKey() const override {
-            return static_cast<int32_t>(engine_) |
-                  (static_cast<int32_t>(kind) << 1) |
-                  (static_cast<int32_t>(op_) << 17) |
-                  (static_cast<int32_t>(lhsIsInt32_) << 25);
-        }
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsInt32)
-          : ICStubCompiler(cx, ICStub::Compare_Int32WithBoolean, engine),
-            op_(op),
-            lhsIsInt32_(lhsIsInt32)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICCompare_Int32WithBoolean>(space, getStubCode(), lhsIsInt32_);
-        }
-    };
-};
-
 // Enum for stubs handling a combination of typed arrays and typed objects.
 enum TypedThingLayout {
     Layout_TypedArray,
     Layout_OutlineTypedObject,
     Layout_InlineTypedObject
 };
 
 void
--- a/js/src/jit/SharedICList.h
+++ b/js/src/jit/SharedICList.h
@@ -10,25 +10,16 @@
 namespace js {
 namespace jit {
 
 // List of IC stub kinds that can run in Baseline and in IonMonkey
 #define IC_SHARED_STUB_KIND_LIST(_)              \
     _(BinaryArith_Fallback)                      \
                                                  \
     _(Compare_Fallback)                          \
-    _(Compare_Int32)                             \
-    _(Compare_Double)                            \
-    _(Compare_NumberWithUndefined)               \
-    _(Compare_String)                            \
-    _(Compare_Symbol)                            \
-    _(Compare_Boolean)                           \
-    _(Compare_Object)                            \
-    _(Compare_ObjectWithUndefined)               \
-    _(Compare_Int32WithBoolean)                  \
                                                  \
     _(GetProp_Fallback)                          \
                                                  \
     _(CacheIR_Regular)                           \
     _(CacheIR_Monitored)                         \
     _(CacheIR_Updated)                           \
                                                  \
 
deleted file mode 100644
--- a/js/src/jit/arm/BaselineIC-arm.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 "jit/BaselineCompiler.h"
-#include "jit/BaselineIC.h"
-#include "jit/BaselineJIT.h"
-#include "jit/Linker.h"
-#include "jit/SharedICHelpers.h"
-
-#include "jit/MacroAssembler-inl.h"
-
-using namespace js;
-using namespace js::jit;
-
-namespace js {
-namespace jit {
-
-// ICCompare_Int32
-
-bool
-ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    // Guard that R0 is an integer and R1 is an integer.
-    Label failure;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
-
-    // Compare payload regs of R0 and R1.
-    Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
-    masm.cmp32(R0.payloadReg(), R1.payloadReg());
-    masm.ma_mov(Imm32(1), R0.payloadReg(), cond);
-    masm.ma_mov(Imm32(0), R0.payloadReg(), Assembler::InvertCondition(cond));
-
-    // Result is implicitly boxed already.
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub.
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-
-    return true;
-}
-
-bool
-ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure, isNaN;
-    masm.ensureDouble(R0, FloatReg0, &failure);
-    masm.ensureDouble(R1, FloatReg1, &failure);
-
-    Register dest = R0.scratchReg();
-
-    Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(op);
-    Assembler::Condition cond = Assembler::ConditionFromDoubleCondition(doubleCond);
-
-    masm.compareDouble(FloatReg0, FloatReg1);
-    masm.ma_mov(Imm32(0), dest);
-    masm.ma_mov(Imm32(1), dest, cond);
-
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub.
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-} // namespace jit
-} // namespace js
deleted file mode 100644
--- a/js/src/jit/arm64/BaselineIC-arm64.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 "jit/SharedIC.h"
-#include "jit/SharedICHelpers.h"
-
-#ifdef JS_SIMULATOR_ARM64
-#include "jit/arm64/Assembler-arm64.h"
-#include "jit/arm64/BaselineCompiler-arm64.h"
-#include "jit/arm64/vixl/Debugger-vixl.h"
-#endif
-
-#include "jit/MacroAssembler-inl.h"
-
-using namespace js;
-using namespace js::jit;
-
-namespace js {
-namespace jit {
-
-// ICCompare_Int32
-
-bool
-ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    // Guard that R0 is an integer and R1 is an integer.
-    Label failure;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
-
-    // Compare payload regs of R0 and R1.
-    Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
-    masm.cmp32(R0.valueReg(), R1.valueReg());
-    masm.Cset(ARMRegister(R0.valueReg(), 32), cond);
-
-    // Result is implicitly boxed already.
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.valueReg(), R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub.
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-
-    return true;
-}
-
-bool
-ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure, isNaN;
-    masm.ensureDouble(R0, FloatReg0, &failure);
-    masm.ensureDouble(R1, FloatReg1, &failure);
-
-    Register dest = R0.valueReg();
-
-    Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(op);
-    Assembler::Condition cond = Assembler::ConditionFromDoubleCondition(doubleCond);
-
-    masm.compareDouble(doubleCond, FloatReg0, FloatReg1);
-    masm.Cset(ARMRegister(dest, 32), cond);
-
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub.
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-} // namespace jit
-} // namespace js
deleted file mode 100644
--- a/js/src/jit/mips32/BaselineIC-mips32.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 "jit/BaselineCompiler.h"
-#include "jit/BaselineIC.h"
-#include "jit/BaselineJIT.h"
-#include "jit/Linker.h"
-#include "jit/SharedICHelpers.h"
-
-using namespace js;
-using namespace js::jit;
-
-namespace js {
-namespace jit {
-
-// ICCompare_Int32
-
-bool
-ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    // Guard that R0 is an integer and R1 is an integer.
-    Label failure;
-    Label conditionTrue;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
-
-    // Compare payload regs of R0 and R1.
-    Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
-    masm.ma_cmp_set(R0.payloadReg(), R0.payloadReg(), R1.payloadReg(), cond);
-
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-
-    return true;
-}
-
-} // namespace jit
-} // namespace js
deleted file mode 100644
--- a/js/src/jit/mips64/BaselineIC-mips64.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 "jit/BaselineCompiler.h"
-#include "jit/BaselineIC.h"
-#include "jit/BaselineJIT.h"
-#include "jit/Linker.h"
-#include "jit/SharedICHelpers.h"
-
-using namespace js;
-using namespace js::jit;
-
-namespace js {
-namespace jit {
-
-// ICCompare_Int32
-
-bool
-ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    // Guard that R0 is an integer and R1 is an integer.
-    Label failure;
-    Label conditionTrue;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
-
-    // Compare payload regs of R0 and R1.
-    masm.unboxInt32(R0, ExtractTemp0);
-    masm.unboxInt32(R1, ExtractTemp1);
-    Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
-    masm.ma_cmp_set(R0.valueReg(), ExtractTemp0, ExtractTemp1, cond);
-
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.valueReg(), R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-
-    return true;
-}
-
-} // namespace jit
-} // namespace js
--- a/js/src/jit/none/Trampoline-none.cpp
+++ b/js/src/jit/none/Trampoline-none.cpp
@@ -37,10 +37,8 @@ BailoutFrameInfo::BailoutFrameInfo(const
     MOZ_CRASH();
 }
 
 BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& iter, InvalidationBailoutStack* bailout)
 {
     MOZ_CRASH();
 }
 
-bool ICCompare_Int32::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }
-bool ICCompare_Double::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -880,20 +880,16 @@ class AssemblerShared
     bool oom() const {
         return !enoughMemory_;
     }
 
     bool embedsNurseryPointers() const {
         return embedsNurseryPointers_;
     }
 
-    static bool canUseInSingleByteInstruction(Register reg) {
-        return true;
-    }
-
     void addCodeLabel(CodeLabel label) {
         propagateOOM(codeLabels_.append(label));
     }
     size_t numCodeLabels() const {
         return codeLabels_.length();
     }
     CodeLabel codeLabel(size_t i) {
         return codeLabels_[i];
deleted file mode 100644
--- a/js/src/jit/x64/BaselineIC-x64.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 "jit/BaselineIC.h"
-#include "jit/SharedICHelpers.h"
-#include "jit/MacroAssembler-inl.h"
-
-using namespace js;
-using namespace js::jit;
-
-namespace js {
-namespace jit {
-
-// ICCompare_Int32
-
-bool
-ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    // Guard that R0 is an integer and R1 is an integer.
-    Label failure;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
-
-    // Directly compare the int32 payload of R0 and R1.
-    ScratchRegisterScope scratch(masm);
-    Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
-    masm.mov(ImmWord(0), scratch);
-    masm.cmp32(R0.valueReg(), R1.valueReg());
-    masm.setCC(cond, scratch);
-
-    // Box the result and return
-    masm.boxValue(JSVAL_TYPE_BOOLEAN, scratch, R0.valueReg());
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-
-    return true;
-}
-
-} // namespace jit
-} // namespace js
deleted file mode 100644
--- a/js/src/jit/x86-shared/BaselineIC-x86-shared.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 "jit/BaselineIC.h"
-#include "jit/SharedICHelpers.h"
-
-#include "jit/MacroAssembler-inl.h"
-
-using namespace js;
-using namespace js::jit;
-
-bool
-ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure, notNaN;
-    masm.ensureDouble(R0, FloatReg0, &failure);
-    masm.ensureDouble(R1, FloatReg1, &failure);
-
-    Register dest = R0.scratchReg();
-
-    Assembler::DoubleCondition cond = JSOpToDoubleCondition(op);
-    masm.mov(ImmWord(0), dest);
-    masm.compareDouble(cond, FloatReg0, FloatReg1);
-    masm.setCC(Assembler::ConditionFromDoubleCondition(cond), dest);
-
-    // Check for NaN, if needed.
-    Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
-    if (nanCond != Assembler::NaN_HandledByCond) {
-      masm.j(Assembler::NoParity, &notNaN);
-      masm.mov(ImmWord(nanCond == Assembler::NaN_IsTrue), dest);
-      masm.bind(&notNaN);
-    }
-
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -1055,20 +1055,16 @@ class Assembler : public AssemblerX86Sha
         masm.vmovdqu_rm(src.encoding(), dest.addr);
         return CodeOffset(masm.currentOffset());
     }
     CodeOffset vmovupsWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) {
         MOZ_ASSERT(HasSSE2());
         masm.vmovups_rm(src.encoding(), dest.addr);
         return CodeOffset(masm.currentOffset());
     }
-
-    static bool canUseInSingleByteInstruction(Register reg) {
-        return X86Encoding::HasSubregL(reg.encoding());
-    }
 };
 
 // Get a register in which we plan to put a quantity that will be used as an
 // integer argument.  This differs from GetIntArgReg in that if we have no more
 // actual argument registers to use we will fall back on using whatever
 // CallTempReg* don't overlap the argument registers, and only fail once those
 // run out too.
 static inline bool
deleted file mode 100644
--- a/js/src/jit/x86/BaselineIC-x86.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 "jit/BaselineCompiler.h"
-#include "jit/BaselineIC.h"
-#include "jit/BaselineJIT.h"
-#include "jit/Linker.h"
-#include "jit/SharedICHelpers.h"
-
-#include "jit/MacroAssembler-inl.h"
-
-using namespace js;
-using namespace js::jit;
-
-namespace js {
-namespace jit {
-
-// ICCompare_Int32
-
-bool
-ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    // Guard that R0 is an integer and R1 is an integer.
-    Label failure;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
-
-    // Compare payload regs of R0 and R1.
-    Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
-    masm.cmp32(R0.payloadReg(), R1.payloadReg());
-    masm.setCC(cond, R0.payloadReg());
-    masm.movzbl(R0.payloadReg(), R0.payloadReg());
-
-    // Box the result and return
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.payloadReg(), R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-} // namespace jit
-} // namespace js
--- a/js/src/jsapi-tests/testSavedStacks.cpp
+++ b/js/src/jsapi-tests/testSavedStacks.cpp
@@ -254,17 +254,17 @@ BEGIN_TEST(testSavedStacks_selfHostedFra
     CHECK(result == JS::SavedFrameResult::Ok);
     CHECK_EQUAL(line, 3U);
 
     // Column
     uint32_t column = 123;
     result = JS::GetSavedFrameColumn(cx, principals, selfHostedFrame, &column,
                                      JS::SavedFrameSelfHosted::Exclude);
     CHECK(result == JS::SavedFrameResult::Ok);
-    CHECK_EQUAL(column, 5U);
+    CHECK_EQUAL(column, 9U);
 
     // Function display name
     result = JS::GetSavedFrameFunctionDisplayName(cx, principals, selfHostedFrame, &str,
                                                   JS::SavedFrameSelfHosted::Exclude);
     CHECK(result == JS::SavedFrameResult::Ok);
     lin = str->ensureLinear(cx);
     CHECK(lin);
     CHECK(js::StringEqualsAscii(lin, "one"));
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -496,58 +496,54 @@ if not CONFIG['ENABLE_ION']:
     ]
 elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
     LOpcodesGenerated.inputs += ['jit/x86-shared/LIR-x86-shared.h']
     UNIFIED_SOURCES += [
         'jit/x86-shared/Architecture-x86-shared.cpp',
         'jit/x86-shared/Assembler-x86-shared.cpp',
         'jit/x86-shared/AssemblerBuffer-x86-shared.cpp',
         'jit/x86-shared/BaselineCompiler-x86-shared.cpp',
-        'jit/x86-shared/BaselineIC-x86-shared.cpp',
         'jit/x86-shared/CodeGenerator-x86-shared.cpp',
         'jit/x86-shared/Lowering-x86-shared.cpp',
         'jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp',
         'jit/x86-shared/MacroAssembler-x86-shared.cpp',
         'jit/x86-shared/MoveEmitter-x86-shared.cpp',
     ]
     SOURCES += [
         'jit/x86-shared/Disassembler-x86-shared.cpp',  # using namespace js::jit::X86Encoding;
     ]
     if CONFIG['JS_CODEGEN_X64']:
         LOpcodesGenerated.inputs += ['jit/x64/LIR-x64.h']
         UNIFIED_SOURCES += [
             'jit/x64/Assembler-x64.cpp',
             'jit/x64/Bailouts-x64.cpp',
             'jit/x64/BaselineCompiler-x64.cpp',
-            'jit/x64/BaselineIC-x64.cpp',
             'jit/x64/CodeGenerator-x64.cpp',
             'jit/x64/Lowering-x64.cpp',
             'jit/x64/MacroAssembler-x64.cpp',
             'jit/x64/Trampoline-x64.cpp',
         ]
     else:
         LOpcodesGenerated.inputs += ['jit/x86/LIR-x86.h']
         UNIFIED_SOURCES += [
             'jit/x86/Assembler-x86.cpp',
             'jit/x86/Bailouts-x86.cpp',
             'jit/x86/BaselineCompiler-x86.cpp',
-            'jit/x86/BaselineIC-x86.cpp',
             'jit/x86/CodeGenerator-x86.cpp',
             'jit/x86/Lowering-x86.cpp',
             'jit/x86/MacroAssembler-x86.cpp',
             'jit/x86/Trampoline-x86.cpp',
         ]
 elif CONFIG['JS_CODEGEN_ARM']:
     LOpcodesGenerated.inputs += ['jit/arm/LIR-arm.h']
     UNIFIED_SOURCES += [
         'jit/arm/Architecture-arm.cpp',
         'jit/arm/Assembler-arm.cpp',
         'jit/arm/Bailouts-arm.cpp',
         'jit/arm/BaselineCompiler-arm.cpp',
-        'jit/arm/BaselineIC-arm.cpp',
         'jit/arm/CodeGenerator-arm.cpp',
         'jit/arm/disasm/Constants-arm.cpp',
         'jit/arm/disasm/Disasm-arm.cpp',
         'jit/arm/Lowering-arm.cpp',
         'jit/arm/MacroAssembler-arm.cpp',
         'jit/arm/MoveEmitter-arm.cpp',
         'jit/arm/Trampoline-arm.cpp',
     ]
@@ -561,17 +557,16 @@ elif CONFIG['JS_CODEGEN_ARM']:
             'jit/arm/llvm-compiler-rt/arm/aeabi_uidivmod.S',
         ]
 elif CONFIG['JS_CODEGEN_ARM64']:
     LOpcodesGenerated.inputs += ['jit/arm64/LIR-arm64.h']
     UNIFIED_SOURCES += [
         'jit/arm64/Architecture-arm64.cpp',
         'jit/arm64/Assembler-arm64.cpp',
         'jit/arm64/Bailouts-arm64.cpp',
-        'jit/arm64/BaselineIC-arm64.cpp',
         'jit/arm64/CodeGenerator-arm64.cpp',
         'jit/arm64/Disassembler-arm64.cpp',
         'jit/arm64/Lowering-arm64.cpp',
         'jit/arm64/MacroAssembler-arm64.cpp',
         'jit/arm64/MoveEmitter-arm64.cpp',
         'jit/arm64/Trampoline-arm64.cpp',
         'jit/arm64/vixl/Assembler-vixl.cpp',
         'jit/arm64/vixl/Cpu-vixl.cpp',
@@ -593,30 +588,28 @@ elif CONFIG['JS_CODEGEN_ARM64']:
         ]
 elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
     LOpcodesGenerated.inputs += ['jit/mips-shared/LIR-mips-shared.h']
     UNIFIED_SOURCES += [
         'jit/mips-shared/Architecture-mips-shared.cpp',
         'jit/mips-shared/Assembler-mips-shared.cpp',
         'jit/mips-shared/Bailouts-mips-shared.cpp',
         'jit/mips-shared/BaselineCompiler-mips-shared.cpp',
-        'jit/mips-shared/BaselineIC-mips-shared.cpp',
         'jit/mips-shared/CodeGenerator-mips-shared.cpp',
         'jit/mips-shared/Lowering-mips-shared.cpp',
         'jit/mips-shared/MacroAssembler-mips-shared.cpp',
         'jit/mips-shared/MoveEmitter-mips-shared.cpp',
     ]
     if CONFIG['JS_CODEGEN_MIPS32']:
         LOpcodesGenerated.inputs += ['jit/mips32/LIR-mips32.h']
         UNIFIED_SOURCES += [
             'jit/mips32/Architecture-mips32.cpp',
             'jit/mips32/Assembler-mips32.cpp',
             'jit/mips32/Bailouts-mips32.cpp',
             'jit/mips32/BaselineCompiler-mips32.cpp',
-            'jit/mips32/BaselineIC-mips32.cpp',
             'jit/mips32/CodeGenerator-mips32.cpp',
             'jit/mips32/Lowering-mips32.cpp',
             'jit/mips32/MacroAssembler-mips32.cpp',
             'jit/mips32/MoveEmitter-mips32.cpp',
             'jit/mips32/Trampoline-mips32.cpp',
         ]
         if CONFIG['JS_SIMULATOR_MIPS32']:
             UNIFIED_SOURCES += [
@@ -624,17 +617,16 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONF
             ]
     elif CONFIG['JS_CODEGEN_MIPS64']:
         LOpcodesGenerated.inputs += ['jit/mips64/LIR-mips64.h']
         UNIFIED_SOURCES += [
             'jit/mips64/Architecture-mips64.cpp',
             'jit/mips64/Assembler-mips64.cpp',
             'jit/mips64/Bailouts-mips64.cpp',
             'jit/mips64/BaselineCompiler-mips64.cpp',
-            'jit/mips64/BaselineIC-mips64.cpp',
             'jit/mips64/CodeGenerator-mips64.cpp',
             'jit/mips64/Lowering-mips64.cpp',
             'jit/mips64/MacroAssembler-mips64.cpp',
             'jit/mips64/MoveEmitter-mips64.cpp',
             'jit/mips64/Trampoline-mips64.cpp',
         ]
         if CONFIG['JS_SIMULATOR_MIPS64']:
             UNIFIED_SOURCES += [
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -4486,17 +4486,17 @@ JSScript::AutoDelazify::dropScript()
 {
     // Don't touch script_ if it's in the self-hosting realm, see the comment
     // in holdScript.
     if (script_ && !script_->realm()->isSelfHostingRealm())
         script_->setDoNotRelazify(oldDoNotRelazify_);
     script_ = nullptr;
 }
 
-JS::ubi::Node::Size
+JS::ubi::Base::Size
 JS::ubi::Concrete<JSScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
 {
     Size size = Arena::thingSize(get().asTenured().getAllocKind());
 
     size += get().sizeOfData(mallocSizeOf);
     size += get().sizeOfTypeScript(mallocSizeOf);
 
     size_t baselineSize = 0;
--- a/js/src/vm/MatchPairs.h
+++ b/js/src/vm/MatchPairs.h
@@ -16,36 +16,38 @@
  * indices delimiting (start, limit] segments of the input string.
  *
  * The pair count for a given RegExp match is the capturing parentheses
  * count plus one for the "0 capturing paren" whole text match.
  */
 
 namespace js {
 
-struct MatchPair
+struct MatchPair final
 {
     int32_t start;
     int32_t limit;
 
+    static constexpr int32_t NoMatch = -1;
+
     MatchPair()
-      : start(-1), limit(-1)
+      : start(NoMatch), limit(NoMatch)
     { }
 
     MatchPair(int32_t start, int32_t limit)
       : start(start), limit(limit)
     { }
 
     size_t length()      const { MOZ_ASSERT(!isUndefined()); return limit - start; }
     bool isUndefined()   const { return start < 0; }
 
     inline bool check() const {
         MOZ_ASSERT(limit >= start);
-        MOZ_ASSERT_IF(start < 0, start == -1);
-        MOZ_ASSERT_IF(limit < 0, limit == -1);
+        MOZ_ASSERT_IF(start < 0, start == NoMatch);
+        MOZ_ASSERT_IF(limit < 0, limit == NoMatch);
         return true;
     }
 };
 
 // MachPairs is used as base class for VectorMatchPairs but can also be
 // stack-allocated (without a Vector) in JIT code.
 class MatchPairs
 {
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -1246,19 +1246,19 @@ RegExpRealm::createMatchResultTemplateOb
     if (!NativeDefineDataProperty(cx, templateObject, cx->names().input, inputVal,
                                   JSPROP_ENUMERATE))
     {
         return matchResultTemplateObject_; // = nullptr
     }
 
     // Make sure that the properties are in the right slots.
     DebugOnly<Shape*> shape = templateObject->lastProperty();
-    MOZ_ASSERT(shape->previous()->slot() == 0 &&
+    MOZ_ASSERT(shape->previous()->slot() == MatchResultObjectIndexSlot &&
                shape->previous()->propidRef() == NameToId(cx->names().index));
-    MOZ_ASSERT(shape->slot() == 1 &&
+    MOZ_ASSERT(shape->slot() == MatchResultObjectInputSlot &&
                shape->propidRef() == NameToId(cx->names().input));
 
     // Make sure type information reflects the indexed properties which might
     // be added.
     AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::StringType());
     AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::UndefinedType());
 
     matchResultTemplateObject_.set(templateObject);
--- a/js/src/vm/RegExpShared.h
+++ b/js/src/vm/RegExpShared.h
@@ -320,16 +320,19 @@ class RegExpRealm
 
     ArrayObject* createMatchResultTemplateObject(JSContext* cx);
 
   public:
     explicit RegExpRealm();
 
     void sweep();
 
+    static const size_t MatchResultObjectIndexSlot = 0;
+    static const size_t MatchResultObjectInputSlot = 1;
+
     /* Get or create template object used to base the result of .exec() on. */
     ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
         if (matchResultTemplateObject_)
             return matchResultTemplateObject_;
         return createMatchResultTemplateObject(cx);
     }
 
     Shape* getOptimizableRegExpPrototypeShape() {
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -411,32 +411,31 @@ ListLength(ParseNode* pn)
     MOZ_ASSERT(pn->isArity(PN_LIST));
     return pn->pn_count;
 }
 
 static inline ParseNode*
 CallCallee(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
-    return ListHead(pn);
+    return BinaryLeft(pn);
 }
 
 static inline unsigned
 CallArgListLength(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
-    MOZ_ASSERT(ListLength(pn) >= 1);
-    return ListLength(pn) - 1;
+    return ListLength(BinaryRight(pn));
 }
 
 static inline ParseNode*
 CallArgList(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
-    return NextNode(ListHead(pn));
+    return ListHead(BinaryRight(pn));
 }
 
 static inline ParseNode*
 VarListHead(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Var) || pn->isKind(ParseNodeKind::Const));
     return ListHead(pn);
 }
@@ -589,26 +588,26 @@ NumberNodeHasFrac(ParseNode* pn)
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Number));
     return pn->pn_u.number.decimalPoint == HasDecimal;
 }
 
 static ParseNode*
 DotBase(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
-    MOZ_ASSERT(pn->isArity(PN_NAME));
-    return pn->expr();
+    MOZ_ASSERT(pn->isArity(PN_BINARY));
+    return pn->pn_left;
 }
 
 static PropertyName*
 DotMember(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
-    MOZ_ASSERT(pn->isArity(PN_NAME));
-    return pn->pn_atom->asPropertyName();
+    MOZ_ASSERT(pn->isArity(PN_BINARY));
+    return pn->pn_right->pn_atom->asPropertyName();
 }
 
 static ParseNode*
 ElemBase(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(ParseNodeKind::Elem));
     return BinaryLeft(pn);
 }
@@ -2795,19 +2794,21 @@ IsArrayViewCtorName(ModuleValidator& m, 
         *type = Scalar::Float64;
     } else {
         return false;
     }
     return true;
 }
 
 static bool
-CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* ctorExpr, PropertyName* bufferName)
-{
-    ParseNode* bufArg = NextNode(ctorExpr);
+CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* newExpr, PropertyName* bufferName)
+{
+    ParseNode* ctorExpr = BinaryLeft(newExpr);
+    ParseNode* ctorArgs = BinaryRight(newExpr);
+    ParseNode* bufArg = ListHead(ctorArgs);
     if (!bufArg || NextNode(bufArg) != nullptr)
         return m.fail(ctorExpr, "array view constructor takes exactly one argument");
 
     if (!IsUseOfName(bufArg, bufferName))
         return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName);
 
     return true;
 }
@@ -2818,17 +2819,17 @@ CheckNewArrayView(ModuleValidator& m, Pr
     PropertyName* globalName = m.globalArgumentName();
     if (!globalName)
         return m.fail(newExpr, "cannot create array view without an asm.js global parameter");
 
     PropertyName* bufferName = m.bufferArgumentName();
     if (!bufferName)
         return m.fail(newExpr, "cannot create array view without an asm.js heap parameter");
 
-    ParseNode* ctorExpr = ListHead(newExpr);
+    ParseNode* ctorExpr = BinaryLeft(newExpr);
 
     PropertyName* field;
     Scalar::Type type;
     if (ctorExpr->isKind(ParseNodeKind::Dot)) {
         ParseNode* base = DotBase(ctorExpr);
 
         if (!IsUseOfName(base, globalName))
             return m.failName(base, "expecting '%s.*Array", globalName);
@@ -2847,17 +2848,17 @@ CheckNewArrayView(ModuleValidator& m, Pr
 
         if (global->which() != ModuleValidator::Global::ArrayViewCtor)
             return m.failName(ctorExpr, "%s must be an imported array view constructor", globalName);
 
         field = nullptr;
         type = global->viewType();
     }
 
-    if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName))
+    if (!CheckNewArrayViewArgs(m, newExpr, bufferName))
         return false;
 
     return m.addArrayView(varName, type, field);
 }
 
 static bool
 CheckGlobalMathImport(ModuleValidator& m, ParseNode* initNode, PropertyName* varName,
                       PropertyName* field)
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -1023,18 +1023,22 @@ ReadScriptOrFunction(nsIObjectInputStrea
     uint8_t flags;
     nsresult rv = stream->Read8(&flags);
     if (NS_FAILED(rv))
         return rv;
 
     // We don't serialize mutedError-ness of scripts, which is fine as long as
     // we only serialize system and XUL-y things. We can detect this by checking
     // where the caller wants us to deserialize.
+    //
+    // CompilationScope() could theoretically GC, so get that out of the way
+    // before comparing to the cx global.
+    JSObject* loaderGlobal = xpc::CompilationScope();
     MOZ_RELEASE_ASSERT(nsContentUtils::IsSystemCaller(cx) ||
-                       CurrentGlobalOrNull(cx) == xpc::CompilationScope());
+                       CurrentGlobalOrNull(cx) == loaderGlobal);
 
     uint32_t size;
     rv = stream->Read32(&size);
     if (NS_FAILED(rv))
         return rv;
 
     char* data;
     rv = stream->ReadBytes(size, &data);
--- a/js/xpconnect/tests/unit/test_getCallerLocation.js
+++ b/js/xpconnect/tests/unit/test_getCallerLocation.js
@@ -34,33 +34,33 @@ add_task(async function() {
   `, sandbox, undefined, "thing.js");
 
   Cu.exportFunction(foo, sandbox, {defineAs: "foo"});
 
   let frame = sandbox.thing();
 
   equal(frame.source, "thing.js", "Frame source");
   equal(frame.line, 5, "Frame line");
-  equal(frame.column, 14, "Frame column");
+  equal(frame.column, 18, "Frame column");
   equal(frame.functionDisplayName, "it", "Frame function name");
   equal(frame.parent, null, "Frame parent");
 
-  equal(String(frame), "it@thing.js:5:14\n", "Stringified frame");
+  equal(String(frame), "it@thing.js:5:18\n", "Stringified frame");
 
 
   // reportError
 
   let {messages} = await AddonTestUtils.promiseConsoleOutput(() => {
     Cu.reportError("Meh", frame);
   });
 
   let [msg] = messages.filter(m => m.message.includes("Meh"));
 
   equal(msg.stack, frame, "reportError stack frame");
-  equal(msg.message, '[JavaScript Error: "Meh" {file: "thing.js" line: 5}]\nit@thing.js:5:14\n');
+  equal(msg.message, '[JavaScript Error: "Meh" {file: "thing.js" line: 5}]\nit@thing.js:5:18\n');
 
   Assert.throws(() => { Cu.reportError("Meh", {}); },
                 err => err.result == Cr.NS_ERROR_INVALID_ARG,
                 "reportError should throw when passed a non-SavedFrame object");
 
 
   // createError
 
@@ -77,10 +77,10 @@ add_task(async function() {
   equal(Cu.getGlobalForObject(error), sandbox,
         "createError creates errors in the global of the SavedFrame");
   equal(error.stack, String(cloned),
         "createError creates errors with the correct stack");
 
   equal(error.message, "Meh", "Error message");
   equal(error.fileName, "thing.js", "Error filename");
   equal(error.lineNumber, 5, "Error line");
-  equal(error.columnNumber, 14, "Error column");
+  equal(error.columnNumber, 18, "Error column");
 });
new file mode 100644
--- /dev/null
+++ b/media/libvpx/bug1480092.patch
@@ -0,0 +1,22 @@
+diff --git a/media/libvpx/libvpx/vp8/common/postproc.c b/media/libvpx/libvpx/vp8/common/postproc.c
+--- a/media/libvpx/libvpx/vp8/common/postproc.c
++++ b/media/libvpx/libvpx/vp8/common/postproc.c
+@@ -60,17 +60,17 @@ static void vp8_de_mblock(YV12_BUFFER_CO
+ }
+ 
+ void vp8_deblock(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source,
+                  YV12_BUFFER_CONFIG *post, int q, int low_var_thresh,
+                  int flag) {
+   double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
+   int ppl = (int)(level + .5);
+ 
+-  const MODE_INFO *mode_info_context = cm->show_frame_mi;
++  const MODE_INFO *mode_info_context = cm->mi;
+   int mbr, mbc;
+ 
+   /* The pixel thresholds are adjusted according to if or not the macroblock
+    * is a skipped block.  */
+   unsigned char *ylimits = cm->pp_limits_buffer;
+   unsigned char *uvlimits = cm->pp_limits_buffer + 16 * cm->mb_cols;
+   (void)low_var_thresh;
+   (void)flag;
--- a/media/libvpx/libvpx/vp8/common/postproc.c
+++ b/media/libvpx/libvpx/vp8/common/postproc.c
@@ -60,17 +60,17 @@ static void vp8_de_mblock(YV12_BUFFER_CO
 }
 
 void vp8_deblock(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source,
                  YV12_BUFFER_CONFIG *post, int q, int low_var_thresh,
                  int flag) {
   double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
   int ppl = (int)(level + .5);
 
-  const MODE_INFO *mode_info_context = cm->show_frame_mi;
+  const MODE_INFO *mode_info_context = cm->mi;
   int mbr, mbc;
 
   /* The pixel thresholds are adjusted according to if or not the macroblock
    * is a skipped block.  */
   unsigned char *ylimits = cm->pp_limits_buffer;
   unsigned char *uvlimits = cm->pp_limits_buffer + 16 * cm->mb_cols;
   (void)low_var_thresh;
   (void)flag;
--- a/media/libvpx/update.py
+++ b/media/libvpx/update.py
@@ -36,16 +36,18 @@ def apply_patches():
     os.system("patch -p3 < bug1137614.patch")
     # Bug 1263384 - Check input frame resolution
     os.system("patch -p3 < input_frame_validation.patch")
     # Bug 1315288 - Check input frame resolution for vp9
     os.system("patch -p3 < input_frame_validation_vp9.patch")
     # Avoid c/asm name collision for loopfilter_sse2
     os.system("patch -p1 < rename_duplicate_files.patch")
     os.system("mv libvpx/vpx_dsp/x86/loopfilter_sse2.c libvpx/vpx_dsp/x86/loopfilter_intrin_sse2.c")
+    # Cherrypick fix from upstream
+    os.system("patch -p3 < bug1480092.patch")
 
 
 def update_readme(commit):
     with open('README_MOZILLA') as f:
         readme = f.read()
 
     if 'The git commit ID used was' in readme:
         new_readme = re.sub('The git commit ID used was [v\.a-f0-9]+',
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
@@ -140,46 +140,30 @@ class NavigationDelegateTest : BaseSessi
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         assertThat("User agent should be set to mobile",
                    sessionRule.session.evaluateJS(userAgentJs) as String,
                    containsString(mobileSubStr))
     }
 
-    fun telemetryTest(process: String) {
+    @Test fun telemetrySnapshots() {
         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
         sessionRule.waitForPageStop()
 
         val telemetry = sessionRule.runtime.telemetry
-        val result = sessionRule.waitForResult(telemetry.getSnapshots(true))
-
-        val snapshots = result?.get(process) as GeckoBundle
+        val result = sessionRule.waitForResult(telemetry.getSnapshots(false))
 
         assertThat("Snapshots should not be null",
-                   snapshots, notNullValue())
+                   result?.get("parent"), notNullValue())
 
-        assertThat("Histograms should not be null",
-                   snapshots.get("histograms"), notNullValue())
-        assertThat("Keyed histograms should not be null",
-                   snapshots.get("keyedHistograms"), notNullValue())
-        assertThat("Scalars should not be null",
-                   snapshots.get("scalars"), notNullValue())
-        assertThat("Keyed scalars should not be null",
-                   snapshots.get("keyedScalars"), notNullValue())
-    }
-
-    @Test fun telemetryParent() {
-        telemetryTest("parent")
-    }
-
-
-    @Test fun telemetryContent() {
-        assumeThat(sessionRule.env.isMultiprocess, equalTo(true));
-        telemetryTest("content")
+        if (sessionRule.env.isMultiprocess) {
+            assertThat("Snapshots should not be null",
+                       result?.get("content"), notNullValue())
+        }
     }
 
     @Test fun load() {
         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
         sessionRule.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
             @AssertCalled(count = 1, order = [1])
--- a/taskcluster/scripts/builder/hazard-analysis.sh
+++ b/taskcluster/scripts/builder/hazard-analysis.sh
@@ -97,17 +97,17 @@ function run_analysis () {
     build_type="$2"
 
     if [[ -z "$HAZ_DEP" ]]; then
         [ -d $MOZ_OBJDIR ] && rm -rf $MOZ_OBJDIR
     fi
 
     (
         cd "$analysis_dir"
-        $PYTHON "$ANALYSIS_SRCDIR/analyze.py" --buildcommand="$GECKO_DIR/taskcluster/scripts/builder/hazard-${build_type}.sh"
+        $PYTHON "$ANALYSIS_SRCDIR/analyze.py" -v --buildcommand="$GECKO_DIR/taskcluster/scripts/builder/hazard-${build_type}.sh"
     )
 }
 
 function grab_artifacts () {
     local analysis_dir
     analysis_dir="$1"
     local artifacts
     artifacts="$2"
--- a/taskcluster/scripts/builder/hazard-shell.sh
+++ b/taskcluster/scripts/builder/hazard-shell.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -ex
 
 mkdir -p "$ANALYZED_OBJDIR"
 cd "$ANALYZED_OBJDIR"
 $SOURCE/js/src/configure --enable-debug --enable-optimize --enable-ctypes --enable-nspr-build
-make -j12 -s
+make -j8 -s
--- a/testing/mozbase/mozdevice/mozdevice/adb.py
+++ b/testing/mozbase/mozdevice/mozdevice/adb.py
@@ -135,34 +135,38 @@ class ADBCommand(object):
     """
 
     def __init__(self,
                  adb='adb',
                  adb_host=None,
                  adb_port=None,
                  logger_name='adb',
                  timeout=300,
-                 verbose=False):
+                 verbose=False,
+                 require_root=True):
         """Initializes the ADBCommand object.
 
         :param str adb: path to adb executable. Defaults to 'adb'.
         :param adb_host: host of the adb server.
         :type adb_host: str or None
         :param adb_port: port of the adb server.
         :type adb_port: integer or None
         :param str logger_name: logging logger name. Defaults to 'adb'.
+        :param bool verbose: provide verbose output
+        :param bool require_root: check that we have root permissions on device
 
         :raises: * ADBError
                  * ADBTimeoutError
         """
         if self.__class__ == ADBCommand:
             raise NotImplementedError
 
         self._logger = self._get_logger(logger_name)
         self._verbose = verbose
+        self._require_root = require_root
         self._adb_path = adb
         self._adb_host = adb_host
         self._adb_port = adb_port
         self._timeout = timeout
         self._polling_interval = 0.1
         self._adb_version = ''
 
         self._logger.debug("%s: %s" % (self.__class__.__name__,
@@ -813,25 +817,25 @@ class ADBDevice(ADBCommand):
             if attempt != max_attempts:
                 time.sleep(20)
 
         raise ADBError("Unable to set up test root using paths: [%s]"
                        % ", ".join(paths))
 
     def _try_test_root(self, test_root):
         base_path, sub_path = posixpath.split(test_root)
-        if not self.is_dir(base_path, root=True):
+        if not self.is_dir(base_path, root=self._require_root):
             return False
 
         try:
             dummy_dir = posixpath.join(test_root, 'dummy')
-            if self.is_dir(dummy_dir, root=True):
-                self.rm(dummy_dir, recursive=True, root=True)
-            self.mkdir(dummy_dir, parents=True, root=True)
-            self.chmod(test_root, recursive=True, root=True)
+            if self.is_dir(dummy_dir, root=self._require_root):
+                self.rm(dummy_dir, recursive=True, root=self._require_root)
+            self.mkdir(dummy_dir, parents=True, root=self._require_root)
+            self.chmod(test_root, recursive=True, root=self._require_root)
         except ADBError:
             self._logger.debug("%s is not writable" % test_root)
             return False
 
         return True
 
     # Host Command methods
 
--- a/testing/mozbase/mozdevice/setup.py
+++ b/testing/mozbase/mozdevice/setup.py
@@ -3,17 +3,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/.
 
 from __future__ import absolute_import
 
 from setuptools import setup
 
 PACKAGE_NAME = 'mozdevice'
-PACKAGE_VERSION = '1.0.0'
+PACKAGE_VERSION = '1.0.1'
 
 deps = ['mozfile >= 1.0',
         'mozlog >= 3.0',
         'moznetwork >= 0.24',
         'mozprocess >= 0.19',
         ]
 
 setup(name=PACKAGE_NAME,
--- a/testing/web-platform/tests/content-security-policy/securitypolicyviolation/targeting.html
+++ b/testing/web-platform/tests/content-security-policy/securitypolicyviolation/targeting.html
@@ -38,17 +38,17 @@
                 assert_equals(e.lineNumber, 118);
                 assert_equals(e.columnNumber, 4);
                 assert_equals(e.target, document, "Elements created in this document, but pushed into a same-origin frame trigger on that frame's document, not on this frame's document.");
                 return watcher.wait_for('securitypolicyviolation');
             }))
             .then(t.step_func(e => {
                 assert_equals(e.blockedURI, "inline");
                 assert_equals(e.lineNumber, 131);
-                assert_equals(e.columnNumber, 4);
+                assert_in_array(e.columnNumber, [4, 59]);
                 assert_equals(e.target, document, "Elements created in this document, but pushed into a same-origin frame trigger on that frame's document, not on this frame's document.");
                 return watcher.wait_for('securitypolicyviolation');
             }))
             .then(t.step_func(e => {
                 assert_equals(e.blockedURI, "inline");
                 assert_equals(e.lineNumber, 139);
                 assert_equals(e.columnNumber, 4);
                 assert_equals(e.target, document, "Inline event handlers for disconnected elements target the document.");
--- a/toolkit/modules/tests/xpcshell/test_NewTabUtils.js
+++ b/toolkit/modules/tests/xpcshell/test_NewTabUtils.js
@@ -1,13 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // See also browser/base/content/test/newtab/.
 
+ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
 // A small 1x1 test png
 const image1x1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
 
 function getBookmarksSize() {
   return NewTabUtils.activityStreamProvider.executePlacesQuery(
     "SELECT count(*) FROM moz_bookmarks WHERE type = :type",
     {params: {type: PlacesUtils.bookmarks.TYPE_BOOKMARK}});
 }
@@ -674,17 +676,18 @@ add_task(async function getTopFrecentSit
   links = await provider.getTopSites();
   Assert.equal(links.length, 0, "adding a single visit doesn't exceed default threshold");
 
   links = await provider.getTopSites({topsiteFrecency: 100});
   Assert.equal(links.length, 1, "adding a visit yields a link");
   Assert.equal(links[0].url, testURI, "added visit corresponds to added url");
 });
 
-add_task(async function getTopFrecentSites_improveSearch() {
+add_task({ skip_if: () => AppConstants.MOZ_APP_NAME == "thunderbird" },
+         async function getTopFrecentSites_improveSearch() {
   await setUpActivityStreamTest();
   const SEARCH_SHORTCUTS_EXPERIMENT_PREF = "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts";
   Services.prefs.setBoolPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF, true);
 
   let testURI = "https://www.amazon.com?search=tv";
   await PlacesTestUtils.addVisits(testURI);
 
   let provider = NewTabUtils.activityStreamLinks;
--- a/toolkit/recordreplay/ProcessRedirectDarwin.cpp
+++ b/toolkit/recordreplay/ProcessRedirectDarwin.cpp
@@ -400,17 +400,19 @@ namespace recordreplay {
   MACRO(OSSpinLockLock)                         \
   MACRO(ReleaseEvent)                           \
   MACRO(RemoveEventFromQueue)                   \
   MACRO(RetainEvent)                            \
   MACRO(SendEventToEventTarget)                 \
   MACRO(SLDisplayCopyColorSpace)                \
   MACRO(SLDisplayIOServicePort)                 \
   MACRO(SLEventSourceCounterForEventType)       \
-  MACRO(SLMainDisplayID)
+  MACRO(SLMainDisplayID)                        \
+  MACRO(SLSSetDenyWindowServerConnections)      \
+  MACRO(SLSShutdownServerConnections)
 
 #define MAKE_CALL_EVENT(aName)  CallEvent_ ##aName ,
 
 enum CallEvent {                                \
   FOR_EACH_REDIRECTION(MAKE_CALL_EVENT)         \
   CallEvent_Count                               \
 };
 
@@ -2674,16 +2676,18 @@ RRFunction2(SendEventToEventTarget)
 
 // These are not public APIs, but other redirected functions may be aliases for
 // these which are dynamically installed on the first call in a way that our
 // redirection mechanism doesn't completely account for.
 RRFunction2(SLEventSourceCounterForEventType)
 RRFunction1(SLDisplayCopyColorSpace)
 RRFunction1(SLDisplayIOServicePort)
 RRFunction0(SLMainDisplayID)
+RRFunction1(SLSSetDenyWindowServerConnections)
+RRFunctionVoid0(SLSShutdownServerConnections)
 
 ///////////////////////////////////////////////////////////////////////////////
 // Redirection generation
 ///////////////////////////////////////////////////////////////////////////////
 
 #define MAKE_REDIRECTION_ENTRY(aName)    \
   { #aName, nullptr, (uint8_t*) RR_ ##aName },
 
--- a/toolkit/system/gnome/nsAlertsIconListener.cpp
+++ b/toolkit/system/gnome/nsAlertsIconListener.cpp
@@ -11,33 +11,37 @@
 #include "nsSystemAlertsService.h"
 #include "nsIAlertsService.h"
 #include "nsICancelable.h"
 #include "nsIImageToPixbuf.h"
 #include "nsIStringBundle.h"
 #include "nsIObserverService.h"
 #include "nsIURI.h"
 #include "nsCRT.h"
+#include "mozilla/XREAppData.h"
 
 #include <dlfcn.h>
 #include <gdk/gdk.h>
 
+extern const mozilla::StaticXREAppData* gAppData;
+
 static bool gHasActions = false;
 static bool gHasCaps = false;
 
 void* nsAlertsIconListener::libNotifyHandle = nullptr;
 bool nsAlertsIconListener::libNotifyNotAvail = false;
 nsAlertsIconListener::notify_is_initted_t nsAlertsIconListener::notify_is_initted = nullptr;
 nsAlertsIconListener::notify_init_t nsAlertsIconListener::notify_init = nullptr;
 nsAlertsIconListener::notify_get_server_caps_t nsAlertsIconListener::notify_get_server_caps = nullptr;
 nsAlertsIconListener::notify_notification_new_t nsAlertsIconListener::notify_notification_new = nullptr;
 nsAlertsIconListener::notify_notification_show_t nsAlertsIconListener::notify_notification_show = nullptr;
 nsAlertsIconListener::notify_notification_set_icon_from_pixbuf_t nsAlertsIconListener::notify_notification_set_icon_from_pixbuf = nullptr;
 nsAlertsIconListener::notify_notification_add_action_t nsAlertsIconListener::notify_notification_add_action = nullptr;
 nsAlertsIconListener::notify_notification_close_t nsAlertsIconListener::notify_notification_close = nullptr;
+nsAlertsIconListener::notify_notification_set_hint_t nsAlertsIconListener::notify_notification_set_hint = nullptr;
 
 static void notify_action_cb(NotifyNotification *notification,
                              gchar *action, gpointer user_data)
 {
   nsAlertsIconListener* alert = static_cast<nsAlertsIconListener*> (user_data);
   alert->SendCallback();
 }
 
@@ -106,16 +110,17 @@ nsAlertsIconListener::nsAlertsIconListen
     notify_is_initted = (notify_is_initted_t)dlsym(libNotifyHandle, "notify_is_initted");
     notify_init = (notify_init_t)dlsym(libNotifyHandle, "notify_init");
     notify_get_server_caps = (notify_get_server_caps_t)dlsym(libNotifyHandle, "notify_get_server_caps");
     notify_notification_new = (notify_notification_new_t)dlsym(libNotifyHandle, "notify_notification_new");
     notify_notification_show = (notify_notification_show_t)dlsym(libNotifyHandle, "notify_notification_show");
     notify_notification_set_icon_from_pixbuf = (notify_notification_set_icon_from_pixbuf_t)dlsym(libNotifyHandle, "notify_notification_set_icon_from_pixbuf");
     notify_notification_add_action = (notify_notification_add_action_t)dlsym(libNotifyHandle, "notify_notification_add_action");
     notify_notification_close = (notify_notification_close_t)dlsym(libNotifyHandle, "notify_notification_close");
+    notify_notification_set_hint = (notify_notification_set_hint_t)dlsym(libNotifyHandle, "notify_notification_set_hint");
     if (!notify_is_initted || !notify_init || !notify_get_server_caps || !notify_notification_new || !notify_notification_show || !notify_notification_set_icon_from_pixbuf || !notify_notification_add_action || !notify_notification_close) {
       dlclose(libNotifyHandle);
       libNotifyHandle = nullptr;
     }
   }
 }
 
 nsAlertsIconListener::~nsAlertsIconListener()
@@ -170,16 +175,30 @@ nsAlertsIconListener::ShowAlert(GdkPixbu
   if (mAlertHasAction) {
     // What we put as the label doesn't matter here, if the action
     // string is "default" then that makes the entire bubble clickable
     // rather than creating a button.
     notify_notification_add_action(mNotification, "default", "Activate",
                                    notify_action_cb, this, nullptr);
   }
 
+  if (notify_notification_set_hint) {
+    // If MOZ_DESKTOP_FILE_NAME variable is set, use it as the application id,
+    // otherwise use gAppData->name
+    if (getenv("MOZ_DESKTOP_FILE_NAME")) {
+      // Send the desktop name to identify the application
+      // The desktop-entry is the part before the .desktop
+      notify_notification_set_hint(mNotification, "desktop-entry",
+                                  g_variant_new("s", getenv("MOZ_DESKTOP_FILE_NAME")));
+    } else {
+      notify_notification_set_hint(mNotification, "desktop-entry",
+                                  g_variant_new("s", gAppData->remotingName));
+    }
+  }
+
   // Fedora 10 calls NotifyNotification "closed" signal handlers with a
   // different signature, so a marshaller is used instead of a C callback to
   // get the user_data (this) in a parseable format.  |closure| is created
   // with a floating reference, which gets sunk by g_signal_connect_closure().
   GClosure* closure = g_closure_new_simple(sizeof(GClosure), this);
   g_closure_set_marshal(closure, notify_closed_marshal);
   mClosureHandler = g_signal_connect_closure(mNotification, "closed", closure, FALSE);
   GError* error = nullptr;
--- a/toolkit/system/gnome/nsAlertsIconListener.h
+++ b/toolkit/system/gnome/nsAlertsIconListener.h
@@ -52,16 +52,17 @@ protected:
   typedef bool (*notify_is_initted_t)(void);
   typedef bool (*notify_init_t)(const char*);
   typedef GList* (*notify_get_server_caps_t)(void);
   typedef NotifyNotification* (*notify_notification_new_t)(const char*, const char*, const char*, const char*);
   typedef bool (*notify_notification_show_t)(void*, GError**);
   typedef void (*notify_notification_set_icon_from_pixbuf_t)(void*, GdkPixbuf*);
   typedef void (*notify_notification_add_action_t)(void*, const char*, const char*, NotifyActionCallback, gpointer, GFreeFunc);
   typedef bool (*notify_notification_close_t)(void*, GError**);
+  typedef void (*notify_notification_set_hint_t)(NotifyNotification*, const char*, GVariant*);
 
   nsCOMPtr<nsICancelable> mIconRequest;
   nsCString mAlertTitle;
   nsCString mAlertText;
 
   nsCOMPtr<nsIObserver> mAlertListener;
   nsString mAlertCookie;
   nsString mAlertName;
@@ -75,16 +76,17 @@ protected:
   static notify_is_initted_t notify_is_initted;
   static notify_init_t notify_init;
   static notify_get_server_caps_t notify_get_server_caps;
   static notify_notification_new_t notify_notification_new;
   static notify_notification_show_t notify_notification_show;
   static notify_notification_set_icon_from_pixbuf_t notify_notification_set_icon_from_pixbuf;
   static notify_notification_add_action_t notify_notification_add_action;
   static notify_notification_close_t notify_notification_close;
+  static notify_notification_set_hint_t notify_notification_set_hint;
   NotifyNotification* mNotification;
   gulong mClosureHandler;
 
   nsresult ShowAlert(GdkPixbuf* aPixbuf);
 
   void NotifyFinished();
 };