Merge mozilla-inbound to mozilla-central. a=merge
authorDaniel Varga <dvarga@mozilla.com>
Tue, 14 Aug 2018 00:59:16 +0300
changeset 486387 8b39d1161075
parent 486343 bb02d7d281cb (current diff)
parent 486386 6f69e8083284 (diff)
child 486401 f4c0098a6088
child 486429 7ee37c654498
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)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
8b39d1161075 / 63.0a1 / 20180813220525 / files
nightly linux64
8b39d1161075 / 63.0a1 / 20180813220525 / files
nightly mac
8b39d1161075 / 63.0a1 / 20180813220525 / files
nightly win32
8b39d1161075 / 63.0a1 / 20180813220525 / files
nightly win64
8b39d1161075 / 63.0a1 / 20180813220525 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge
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();
 };