Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 07 Mar 2017 16:40:44 -0800
changeset 346385 58753259bfeb3b818eac7870871b0aae1f8de64a
parent 346343 dd92d0734a266dab534af530c8ae35670aedd282 (current diff)
parent 346384 26ee17ce7b5b4dbfbbfd2f1c2dbbff9604becda5 (diff)
child 346386 5dc5249408ecc0003f0c68d32a8e782dec23fd36
child 346401 4dc0edd4681c71fb0e67bc19bbc290616952fdf7
child 346495 fed3397d5988bc178b1fe865297f2d2a25c11e13
push id31465
push userkwierso@gmail.com
push dateWed, 08 Mar 2017 00:40:52 +0000
treeherdermozilla-central@58753259bfeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
58753259bfeb / 55.0a1 / 20170308110300 / files
nightly linux64
58753259bfeb / 55.0a1 / 20170308110300 / files
nightly mac
58753259bfeb / 55.0a1 / 20170308030207 / files
nightly win32
58753259bfeb / 55.0a1 / 20170308030207 / files
nightly win64
58753259bfeb / 55.0a1 / 20170308030207 / 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 inbound to central, a=merge MozReview-Commit-ID: 96co4Bbby7W
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -6,16 +6,17 @@ module.exports = {
     "mozilla"
   ],
   "rules": {
     "mozilla/avoid-removeChild": "error",
     "mozilla/import-globals": "warn",
     "mozilla/no-import-into-var-and-global": "error",
     "mozilla/no-useless-parameters": "error",
     "mozilla/no-useless-removeEventListener": "error",
+    "mozilla/use-default-preference-values": "error",
     "mozilla/use-ownerGlobal": "error",
 
     // No (!foo in bar) or (!object instanceof Class)
     "no-unsafe-negation": "error",
   },
   "env": {
     "es6": true
   },
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -10,16 +10,20 @@
 
 const {utils: Cu, interfaces: Ci} = Components;
 
 this.EXPORTED_SYMBOLS = ['AccessFu']; // jshint ignore:line
 
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/accessibility/Utils.jsm');
 
+if (Utils.MozBuildApp === 'mobile/android') {
+  Cu.import('resource://gre/modules/Messaging.jsm');
+}
+
 const ACCESSFU_DISABLE = 0; // jshint ignore:line
 const ACCESSFU_ENABLE = 1;
 const ACCESSFU_AUTO = 2;
 
 const SCREENREADER_SETTING = 'accessibility.screenreader';
 const QUICKNAV_MODES_PREF = 'accessibility.accessfu.quicknav_modes';
 const QUICKNAV_INDEX_PREF = 'accessibility.accessfu.quicknav_index';
 
@@ -27,21 +31,19 @@ this.AccessFu = { // jshint ignore:line
   /**
    * Initialize chrome-layer accessibility functionality.
    * If accessibility is enabled on the platform, then a special accessibility
    * mode is started.
    */
   attach: function attach(aWindow) {
     Utils.init(aWindow);
 
-    try {
-      Services.androidBridge.dispatch('Accessibility:Ready');
-      Services.obs.addObserver(this, 'Accessibility:Settings', false);
-    } catch (x) {
-      // Not on Android
+    if (Utils.MozBuildApp === 'mobile/android') {
+      EventDispatcher.instance.dispatch('Accessibility:Ready');
+      EventDispatcher.instance.registerListener(this, 'Accessibility:Settings');
     }
 
     this._activatePref = new PrefCache(
       'accessibility.accessfu.activate', this._enableOrDisable.bind(this));
 
     this._enableOrDisable();
   },
 
@@ -49,17 +51,17 @@ this.AccessFu = { // jshint ignore:line
    * Shut down chrome-layer accessibility functionality from the outside.
    */
   detach: function detach() {
     // Avoid disabling twice.
     if (this._enabled) {
       this._disable();
     }
     if (Utils.MozBuildApp === 'mobile/android') {
-      Services.obs.removeObserver(this, 'Accessibility:Settings');
+      EventDispatcher.instance.unregisterListener(this, 'Accessibility:Settings');
     }
     delete this._activatePref;
     Utils.uninit();
   },
 
   /**
    * A lazy getter for event handler that binds the scope to AccessFu object.
    */
@@ -115,26 +117,31 @@ this.AccessFu = { // jshint ignore:line
     this._notifyOutputPref =
       new PrefCache('accessibility.accessfu.notify_output');
 
 
     this.Input.start();
     Output.start();
     PointerAdapter.start();
 
+    if (Utils.MozBuildApp === 'mobile/android') {
+      EventDispatcher.instance.registerListener(this, [
+        'Accessibility:ActivateObject',
+        'Accessibility:Focus',
+        'Accessibility:LongPress',
+        'Accessibility:MoveByGranularity',
+        'Accessibility:NextObject',
+        'Accessibility:PreviousObject',
+        'Accessibility:ScrollBackward',
+        'Accessibility:ScrollForward',
+      ]);
+    }
+
     Services.obs.addObserver(this, 'remote-browser-shown', false);
     Services.obs.addObserver(this, 'inprocess-browser-shown', false);
-    Services.obs.addObserver(this, 'Accessibility:NextObject', false);
-    Services.obs.addObserver(this, 'Accessibility:PreviousObject', false);
-    Services.obs.addObserver(this, 'Accessibility:Focus', false);
-    Services.obs.addObserver(this, 'Accessibility:ActivateObject', false);
-    Services.obs.addObserver(this, 'Accessibility:LongPress', false);
-    Services.obs.addObserver(this, 'Accessibility:ScrollForward', false);
-    Services.obs.addObserver(this, 'Accessibility:ScrollBackward', false);
-    Services.obs.addObserver(this, 'Accessibility:MoveByGranularity', false);
     Utils.win.addEventListener('TabOpen', this);
     Utils.win.addEventListener('TabClose', this);
     Utils.win.addEventListener('TabSelect', this);
 
     if (this.readyCallback) {
       this.readyCallback();
       delete this.readyCallback;
     }
@@ -164,24 +171,29 @@ this.AccessFu = { // jshint ignore:line
     PointerAdapter.stop();
 
     Utils.win.removeEventListener('TabOpen', this);
     Utils.win.removeEventListener('TabClose', this);
     Utils.win.removeEventListener('TabSelect', this);
 
     Services.obs.removeObserver(this, 'remote-browser-shown');
     Services.obs.removeObserver(this, 'inprocess-browser-shown');
-    Services.obs.removeObserver(this, 'Accessibility:NextObject');
-    Services.obs.removeObserver(this, 'Accessibility:PreviousObject');
-    Services.obs.removeObserver(this, 'Accessibility:Focus');
-    Services.obs.removeObserver(this, 'Accessibility:ActivateObject');
-    Services.obs.removeObserver(this, 'Accessibility:LongPress');
-    Services.obs.removeObserver(this, 'Accessibility:ScrollForward');
-    Services.obs.removeObserver(this, 'Accessibility:ScrollBackward');
-    Services.obs.removeObserver(this, 'Accessibility:MoveByGranularity');
+
+    if (Utils.MozBuildApp === 'mobile/android') {
+      EventDispatcher.instance.unregisterListener(this, [
+        'Accessibility:ActivateObject',
+        'Accessibility:Focus',
+        'Accessibility:LongPress',
+        'Accessibility:MoveByGranularity',
+        'Accessibility:NextObject',
+        'Accessibility:PreviousObject',
+        'Accessibility:ScrollBackward',
+        'Accessibility:ScrollForward',
+      ]);
+    }
 
     delete this._quicknavModesPref;
     delete this._notifyOutputPref;
 
     if (this.doneCallback) {
       this.doneCallback();
       delete this.doneCallback;
     }
@@ -283,53 +295,57 @@ this.AccessFu = { // jshint ignore:line
 
   _handleMessageManager: function _handleMessageManager(aMessageManager) {
     if (this._enabled) {
       this._addMessageListeners(aMessageManager);
     }
     this._loadFrameScript(aMessageManager);
   },
 
-  observe: function observe(aSubject, aTopic, aData) {
-    switch (aTopic) {
+  onEvent: function (event, data, callback) {
+    switch (event) {
       case 'Accessibility:Settings':
-        this._systemPref = JSON.parse(aData).enabled;
+        this._systemPref = data.enabled;
         this._enableOrDisable();
         break;
       case 'Accessibility:NextObject':
-      case 'Accessibility:PreviousObject':
-      {
-        let rule = aData ?
-          aData.substr(0, 1).toUpperCase() + aData.substr(1).toLowerCase() :
+      case 'Accessibility:PreviousObject': {
+        let rule = data ?
+          data.rule.substr(0, 1).toUpperCase() + data.rule.substr(1).toLowerCase() :
           'Simple';
-        let method = aTopic.replace(/Accessibility:(\w+)Object/, 'move$1');
+        let method = event.replace(/Accessibility:(\w+)Object/, 'move$1');
         this.Input.moveCursor(method, rule, 'gesture');
         break;
       }
       case 'Accessibility:ActivateObject':
-        this.Input.activateCurrent(JSON.parse(aData));
+        this.Input.activateCurrent(data);
         break;
       case 'Accessibility:LongPress':
         this.Input.sendContextMenuMessage();
         break;
       case 'Accessibility:ScrollForward':
         this.Input.androidScroll('forward');
         break;
       case 'Accessibility:ScrollBackward':
         this.Input.androidScroll('backward');
         break;
       case 'Accessibility:Focus':
-        this._focused = JSON.parse(aData);
+        this._focused = data.gainFocus;
         if (this._focused) {
           this.autoMove({ forcePresent: true, noOpIfOnScreen: true });
         }
         break;
       case 'Accessibility:MoveByGranularity':
-        this.Input.moveByGranularity(JSON.parse(aData));
+        this.Input.moveByGranularity(data);
         break;
+    }
+  },
+
+  observe: function observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
       case 'remote-browser-shown':
       case 'inprocess-browser-shown':
       {
         // Ignore notifications that aren't from a Browser
         let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader);
         if (!frameLoader.ownerIsMozBrowserFrame) {
           return;
         }
--- a/accessible/tests/mochitest/jsat/output.js
+++ b/accessible/tests/mochitest/jsat/output.js
@@ -55,16 +55,17 @@ function testContextOutput(expected, aAc
 function testObjectOutput(aAccOrElmOrID, aGenerator) {
   var accessible = getAccessible(aAccOrElmOrID);
   if (!accessible.name || !accessible.name.trim()) {
     return;
   }
   var context = new PivotContext(accessible);
   var output = aGenerator.genForObject(accessible, context);
   var outputOrder;
+  // eslint-disable-next-line mozilla/use-default-preference-values
   try {
     outputOrder = SpecialPowers.getIntPref(PREF_UTTERANCE_ORDER);
   } catch (ex) {
     // PREF_UTTERANCE_ORDER not set.
     outputOrder = 0;
   }
   var expectedNameIndex = outputOrder === 0 ? output.length - 1 : 0;
   var nameIndex = output.indexOf(accessible.name);
--- a/addon-sdk/source/test/test-weak-set.js
+++ b/addon-sdk/source/test/test-weak-set.js
@@ -76,71 +76,71 @@ exports['test add/remove/iterate/clear i
 };
 
 exports['test adding non object or null item'] = function(assert) {
   let items = {};
 
   assert.throws(() => {
     add(items, 'foo');
   },
-  /^\w+ is not a non-null object/,
+  TypeError,
   'only non-null object are allowed');
 
   assert.throws(() => {
     add(items, 0);
   },
-  /^\w+ is not a non-null object/,
+  TypeError,
   'only non-null object are allowed');
 
   assert.throws(() => {
     add(items, undefined);
   },
-  /^\w+ is not a non-null object/,
+  TypeError,
   'only non-null object are allowed');
 
   assert.throws(() => {
     add(items, null);
   },
-  /^\w+ is not a non-null object/,
+  TypeError,
   'only non-null object are allowed');
 
   assert.throws(() => {
     add(items, true);
   },
-  /^\w+ is not a non-null object/,
+  TypeError,
   'only non-null object are allowed');
 };
 
 exports['test adding to non object or null item'] = function(assert) {
   let item = {};
 
   assert.throws(() => {
     add('foo', item);
   },
-  /^\w+ is not a non-null object/,
+  TypeError,
   'only non-null object are allowed');
 
   assert.throws(() => {
     add(0, item);
   },
-  /^\w+ is not a non-null object/,
+  TypeError,
   'only non-null object are allowed');
 
   assert.throws(() => {
     add(undefined, item);
   },
-  /^\w+ is not a non-null object/,
+  TypeError,
   'only non-null object are allowed');
 
   assert.throws(() => {
     add(null, item);
   },
-  /^\w+ is not a non-null object/,
+  TypeError,
   'only non-null object are allowed');
 
   assert.throws(() => {
     add(true, item);
   },
-  /^\w+ is not a non-null object/,
+  TypeError,
   'only non-null object are allowed');
 };
 
 require("sdk/test").run(exports);
--- a/b2g/chrome/content/devtools/adb.js
+++ b/b2g/chrome/content/devtools/adb.js
@@ -41,16 +41,17 @@ var AdbController = {
     this.updateState();
   },
 
   startDisableAdbTimer: function() {
     if (this.disableAdbTimer) {
       this.disableAdbTimer.cancel();
     } else {
       this.disableAdbTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      // eslint-disable-next-line mozilla/use-default-preference-values
       try {
         this.disableAdbTimeoutHours =
           Services.prefs.getIntPref("b2g.adb.timeout-hours");
       } catch (e) {
         // This happens if the pref doesn't exist, in which case
         // disableAdbTimeoutHours will still be set to the default.
       }
     }
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -422,22 +422,18 @@ setUpdateTrackingId();
   let geckoPrefName = 'toolkit.telemetry.enabled';
   SettingsListener.observe(gaiaSettingName, null, function(value) {
     if (value !== null) {
       // Gaia setting has been set; update Gecko pref to that.
       Services.prefs.setBoolPref(geckoPrefName, value);
       return;
     }
     // Gaia setting has not been set; set the gaia setting to default.
-    let prefValue = AppConstants.MOZ_TELEMETRY_ON_BY_DEFAULT;
-    try {
-      prefValue = Services.prefs.getBoolPref(geckoPrefName);
-    } catch (e) {
-      // Pref not set; use default value.
-    }
+    let prefValue = Services.prefs.getBoolPref(geckoPrefName,
+                                               AppConstants.MOZ_TELEMETRY_ON_BY_DEFAULT);
     let setting = {};
     setting[gaiaSettingName] = prefValue;
     window.navigator.mozSettings.createLock().set(setting);
   });
 })();
 
 // =================== Low-precision buffer ======================
 (function setupLowPrecisionSettings() {
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -251,25 +251,21 @@ var shell = {
     SafeMode.check(window).then(() => {
       let startManifestURL =
         Cc['@mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap']
           .getService(Ci.nsISupports).wrappedJSObject.startManifestURL;
 
       // If --start-manifest hasn't been specified, we re-use the latest specified manifest.
       // If it's the first launch, we will fallback to b2g.default.start_manifest_url
       if (AppConstants.MOZ_GRAPHENE && !startManifestURL) {
-        try {
-          startManifestURL = Services.prefs.getCharPref("b2g.system_manifest_url");
-        } catch(e) {}
+        startManifestURL = Services.prefs.getCharPref("b2g.system_manifest_url", "");
       }
 
       if (!startManifestURL) {
-        try {
-          startManifestURL = Services.prefs.getCharPref("b2g.default.start_manifest_url");
-        } catch(e) {}
+        startManifestURL = Services.prefs.getCharPref("b2g.default.start_manifest_url", "");
       }
 
       if (startManifestURL) {
         Cu.import('resource://gre/modules/Bootstraper.jsm');
 
         if (AppConstants.MOZ_GRAPHENE && Bootstraper.isInstallRequired(startManifestURL)) {
           // Installing the app my take some time. We don't want to keep the
           // native window hidden.
--- a/b2g/components/AboutServiceWorkers.jsm
+++ b/b2g/components/AboutServiceWorkers.jsm
@@ -42,20 +42,17 @@ function serializeServiceWorkerInfo(aSer
 }
 
 
 this.AboutServiceWorkers = {
   get enabled() {
     if (this._enabled) {
       return this._enabled;
     }
-    this._enabled = false;
-    try {
-      this._enabled = Services.prefs.getBoolPref("dom.serviceWorkers.enabled");
-    } catch(e) {}
+    this._enabled = Services.prefs.getBoolPref("dom.serviceWorkers.enabled", false);
     return this._enabled;
   },
 
   init: function() {
     SystemAppProxy.addEventListener("mozAboutServiceWorkersContentEvent",
                                     AboutServiceWorkers);
   },
 
--- a/b2g/components/Bootstraper.jsm
+++ b/b2g/components/Bootstraper.jsm
@@ -70,16 +70,17 @@ this.Bootstraper = {
     * If a system app is already installed, uninstall it so that we can
     * cleanly replace it by the current one.
     */
   uninstallPreviousSystemApp: function() {
     // TODO: FIXME
     return Promise.resolve();
 
     let oldManifestURL;
+    // eslint-disable-next-line mozilla/use-default-preference-values
     try{
       oldManifestURL = Services.prefs.getCharPref("b2g.system_manifest_url");
     } catch(e) {
       // No preference set, so nothing to uninstall.
       return Promise.resolve();
     }
 
     let id = DOMApplicationRegistry.getAppLocalIdByManifestURL(oldManifestURL);
--- a/b2g/components/DirectoryProvider.js
+++ b/b2g/components/DirectoryProvider.js
@@ -108,23 +108,17 @@ DirectoryProvider.prototype = {
   },
 
   getFileNotGonk: function(prop, persistent) {
     // In desktop builds, coreAppsDir is the same as the profile
     // directory unless otherwise specified. We just need to get the
     // path from the parent, and it is then used to build
     // jar:remoteopenfile:// uris.
     if (prop == "coreAppsDir") {
-      let coreAppsDirPref;
-      try {
-        coreAppsDirPref = Services.prefs.getCharPref(COREAPPSDIR_PREF);
-      } catch (e) {
-        // coreAppsDirPref may not exist if we're on an older version
-        // of gaia, so just fail silently.
-      }
+      let coreAppsDirPref = Services.prefs.getCharPref(COREAPPSDIR_PREF, "");
       let appsDir;
       // If pref doesn't exist or isn't set, default to old value
       if (!coreAppsDirPref || coreAppsDirPref == "") {
         appsDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
         appsDir.append("webapps");
       } else {
         appsDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
         appsDir.initWithPath(coreAppsDirPref);
--- a/b2g/components/SignInToWebsite.jsm
+++ b/b2g/components/SignInToWebsite.jsm
@@ -89,22 +89,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
 
 // The default persona uri; can be overwritten with toolkit.identity.uri pref.
 // Do this if you want to repoint to a different service for testing.
 // There's no point in setting up an observer to monitor the pref, as b2g prefs
 // can only be overwritten when the profie is recreated.  So just get the value
 // on start-up.
-var kPersonaUri = "https://firefoxos.persona.org";
-try {
-  kPersonaUri = Services.prefs.getCharPref("toolkit.identity.uri");
-} catch(noSuchPref) {
-  // stick with the default value
-}
+var kPersonaUri = Services.prefs.getCharPref("toolkit.identity.uri",
+                                             "https://firefoxos.persona.org");
 
 // JS shim that contains the callback functions that
 // live within the identity UI provisioning frame.
 const kIdentityShimFile = "chrome://b2g/content/identity.js";
 
 // Type of MozChromeEvents to handle id dialogs.
 const kOpenIdentityDialog = "id-dialog-open";
 const kDoneIdentityDialog = "id-dialog-done";
--- a/browser/base/content/aboutaccounts/aboutaccounts.js
+++ b/browser/base/content/aboutaccounts/aboutaccounts.js
@@ -427,24 +427,19 @@ function show(id, childId) {
 // Migrate sync data from the default profile to the dev-edition profile.
 // Returns a promise of a true value if migration succeeded, or false if it
 // failed.
 function migrateToDevEdition(urlParams) {
   let defaultProfilePath;
   try {
     defaultProfilePath = window.getDefaultProfilePath();
   } catch (e) {} // no default profile.
-  let migrateSyncCreds = false;
-  if (defaultProfilePath) {
-    try {
-      migrateSyncCreds = Services.prefs.getBoolPref("identity.fxaccounts.migrateToDevEdition");
-    } catch (e) {}
-  }
 
-  if (!migrateSyncCreds) {
+  if (!defaultProfilePath ||
+      !Services.prefs.getBoolPref("identity.fxaccounts.migrateToDevEdition", false)) {
     return Promise.resolve(false);
   }
 
   Cu.import("resource://gre/modules/osfile.jsm");
   let fxAccountsStorage = OS.Path.join(defaultProfilePath, fxAccountsCommon.DEFAULT_STORAGE_FILENAME);
   return OS.File.read(fxAccountsStorage, { encoding: "utf-8" }).then(text => {
     let accountData = JSON.parse(text).accountData;
     updateDisplayedEmail(accountData);
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -152,20 +152,17 @@ var gFxAccounts = {
 
   handleEvent(event) {
     this._inCustomizationMode = event.type == "customizationstarting";
     this.updateUI();
   },
 
   // Note that updateUI() returns a Promise that's only used by tests.
   updateUI() {
-    let profileInfoEnabled = false;
-    try {
-      profileInfoEnabled = Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled");
-    } catch (e) { }
+    let profileInfoEnabled = Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled", false);
 
     this.panelUIFooter.hidden = false;
 
     // Make sure the button is disabled in customization mode.
     if (this._inCustomizationMode) {
       this.panelUIStatus.setAttribute("disabled", "true");
       this.panelUILabel.setAttribute("disabled", "true");
       this.panelUIAvatar.setAttribute("disabled", "true");
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -292,20 +292,17 @@ var gSyncUI = {
      button being in "customize purgatory" and if so, move it to the panel.
      This is done primarily for profiles created before SyncedTabs landed,
      where the button defaulted to being in that purgatory.
      We use a preference to ensure we only do it once, so people can still
      customize it away and have it stick.
   */
   maybeMoveSyncedTabsButton() {
     const prefName = "browser.migrated-sync-button";
-    let migrated = false;
-    try {
-      migrated = Services.prefs.getBoolPref(prefName);
-    } catch (_) {}
+    let migrated = Services.prefs.getBoolPref(prefName, false);
     if (migrated) {
       return;
     }
     if (!CustomizableUI.getPlacementOfWidget("sync-button")) {
       CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
     }
     Services.prefs.setBoolPref(prefName, true);
   },
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2788,25 +2788,20 @@ var gMenuButtonBadgeManager = {
 // Setup the hamburger button badges for updates, if enabled.
 var gMenuButtonUpdateBadge = {
   enabled: false,
   badgeWaitTime: 0,
   timer: null,
   cancelObserverRegistered: false,
 
   init() {
-    try {
-      this.enabled = Services.prefs.getBoolPref("app.update.badge");
-    } catch (e) {}
+    this.enabled = Services.prefs.getBoolPref("app.update.badge", false);
     if (this.enabled) {
-      try {
-        this.badgeWaitTime = Services.prefs.getIntPref("app.update.badgeWaitTime");
-      } catch (e) {
-        this.badgeWaitTime = 345600; // 4 days
-      }
+      this.badgeWaitTime = Services.prefs.getIntPref("app.update.badgeWaitTime",
+                                                     345600); // 4 days
       Services.obs.addObserver(this, "update-staged", false);
       Services.obs.addObserver(this, "update-downloaded", false);
     }
   },
 
   uninit() {
     if (this.timer)
       this.timer.cancel();
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -1,17 +1,16 @@
 # to be included inside a popupset element
 
     <panel id="notification-popup"
            type="arrow"
            position="after_start"
            hidden="true"
            orient="vertical"
            noautofocus="true"
-           followanchor="false"
            role="alert"/>
 
     <popupnotification id="webRTC-shareDevices-notification" hidden="true">
       <popupnotificationcontent id="webRTC-selectCamera" orient="vertical">
         <label value="&getUserMedia.selectCamera.label;"
                accesskey="&getUserMedia.selectCamera.accesskey;"
                control="webRTC-selectCamera-menulist"/>
         <menulist id="webRTC-selectCamera-menulist">
--- a/browser/base/content/sync/aboutSyncTabs.js
+++ b/browser/base/content/sync/aboutSyncTabs.js
@@ -297,22 +297,17 @@ var RemoteTabViewer = {
 
       el = el.nextSibling;
     }
   },
 
   _refetchTabs(force) {
     if (!force) {
       // Don't bother refetching tabs if we already did so recently
-      let lastFetch = 0;
-      try {
-        lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch");
-      } catch (e) {
-        /* Just use the default value of 0 */
-      }
+      let lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch", 0);
 
       let now = Math.floor(Date.now() / 1000);
       if (now - lastFetch < 30) {
         return false;
       }
     }
 
     // Ask Sync to just do the tabs engine if it can.
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4465,21 +4465,17 @@
             _useDumpForLogging: false,
             _logInit: false,
 
             logging() {
               if (this._useDumpForLogging)
                 return true;
               if (this._logInit)
                 return this._shouldLog;
-              let result = false;
-              try {
-                result = Services.prefs.getBoolPref("browser.tabs.remote.logSwitchTiming");
-              } catch (ex) {
-              }
+              let result = Services.prefs.getBoolPref("browser.tabs.remote.logSwitchTiming", false);
               this._shouldLog = result;
               this._logInit = true;
               return this._shouldLog;
             },
 
             tinfo(tab) {
               if (tab) {
                 return tab._tPos + "(" + tab.linkedBrowser.currentURI.spec + ")";
@@ -5642,21 +5638,17 @@
 
           var tab = this.firstChild;
           tab.label = this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle");
           tab.setAttribute("onerror", "this.removeAttribute('image');");
 
           window.addEventListener("resize", this);
           window.addEventListener("load", this);
 
-          try {
-            this._tabAnimationLoggingEnabled = Services.prefs.getBoolPref("browser.tabs.animationLogging.enabled");
-          } catch (ex) {
-            this._tabAnimationLoggingEnabled = false;
-          }
+          this._tabAnimationLoggingEnabled = Services.prefs.getBoolPref("browser.tabs.animationLogging.enabled", false);
           this._browserNewtabpageEnabled = Services.prefs.getBoolPref("browser.newtabpage.enabled");
           Services.prefs.addObserver("privacy.userContext", this, false);
           this.observe(null, "nsPref:changed", "privacy.userContext.enabled");
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
--- a/browser/base/content/test/general/offlineByDefault.js
+++ b/browser/base/content/test/general/offlineByDefault.js
@@ -1,17 +1,13 @@
 var offlineByDefault = {
   defaultValue: false,
   prefBranch: SpecialPowers.Cc["@mozilla.org/preferences-service;1"].getService(SpecialPowers.Ci.nsIPrefBranch),
   set(allow) {
-    try {
-      this.defaultValue = this.prefBranch.getBoolPref("offline-apps.allow_by_default");
-    } catch (e) {
-      this.defaultValue = false
-    }
+    this.defaultValue = this.prefBranch.getBoolPref("offline-apps.allow_by_default", false);
     this.prefBranch.setBoolPref("offline-apps.allow_by_default", allow);
   },
   reset() {
     this.prefBranch.setBoolPref("offline-apps.allow_by_default", this.defaultValue);
   }
 }
 
 offlineByDefault.set(false);
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -153,20 +153,17 @@ var gUIStateBeforeReset = {
   uiCustomizationState: null,
   drawInTitlebar: null,
   currentTheme: null,
 };
 
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let scope = {};
   Cu.import("resource://gre/modules/Console.jsm", scope);
-  let debug;
-  try {
-    debug = Services.prefs.getBoolPref(kPrefCustomizationDebug);
-  } catch (ex) {}
+  let debug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
   let consoleOptions = {
     maxLogLevel: debug ? "all" : "log",
     prefix: "CustomizableUI",
   };
   return new scope.ConsoleAPI(consoleOptions);
 });
 
 var CustomizableUIInternal = {
@@ -1921,26 +1918,20 @@ var CustomizableUIInternal = {
   // Note that this does not populate gPlacements, which is done lazily so that
   // the legacy state can be migrated, which is only available once a browser
   // window is openned.
   // The panel area is an exception here, since it has no legacy state and is
   // built lazily - and therefore wouldn't otherwise result in restoring its
   // state immediately when a browser window opens, which is important for
   // other consumers of this API.
   loadSavedState() {
-    let state = null;
-    try {
-      state = Services.prefs.getCharPref(kPrefCustomizationState);
-    } catch (e) {
+    let state = Services.prefs.getCharPref(kPrefCustomizationState, "");
+    if (!state) {
       log.debug("No saved state found");
-      // This will fail if nothing has been customized, so silently fall back to
-      // the defaults.
-    }
-
-    if (!state) {
+      // Nothing has been customized, so silently fall back to the defaults.
       return;
     }
     try {
       gSavedState = JSON.parse(state);
       if (typeof gSavedState != "object" || gSavedState === null) {
         throw "Invalid saved state";
       }
     } catch (e) {
@@ -2215,20 +2206,17 @@ var CustomizableUIInternal = {
     // onWidgetAdded event - our own handler will take care of adding it to
     // any build areas.
     this.beginBatchUpdate();
     try {
       if (widget.currentArea) {
         this.notifyListeners("onWidgetAdded", widget.id, widget.currentArea,
                              widget.currentPosition);
       } else if (widgetMightNeedAutoAdding) {
-        let autoAdd = true;
-        try {
-          autoAdd = Services.prefs.getBoolPref(kPrefCustomizationAutoAdd);
-        } catch (e) {}
+        let autoAdd = Services.prefs.getBoolPref(kPrefCustomizationAutoAdd, true);
 
         // If the widget doesn't have an existing placement, and it hasn't been
         // seen before, then add it to its default area so it can be used.
         // If the widget is not removable, we *have* to add it to its default
         // area here.
         let canBeAutoAdded = autoAdd && !gSeenWidgets.has(widget.id);
         if (!widget.currentArea && (!widget.removable || canBeAutoAdded)) {
           if (widget.defaultArea) {
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -41,20 +41,17 @@ XPCOMUtils.defineLazyGetter(this, "Brand
 
 const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const kPrefCustomizationDebug = "browser.uiCustomization.debug";
 const kWidePanelItemClass = "panel-wide-item";
 
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let scope = {};
   Cu.import("resource://gre/modules/Console.jsm", scope);
-  let debug;
-  try {
-    debug = Services.prefs.getBoolPref(kPrefCustomizationDebug);
-  } catch (ex) {}
+  let debug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
   let consoleOptions = {
     maxLogLevel: debug ? "all" : "log",
     prefix: "CustomizableWidgets",
   };
   return new scope.ConsoleAPI(consoleOptions);
 });
 
 
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -37,19 +37,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/LightweightThemeManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
                                   "resource:///modules/sessionstore/SessionStore.jsm");
 
 let gDebug;
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let scope = {};
   Cu.import("resource://gre/modules/Console.jsm", scope);
-  try {
-    gDebug = Services.prefs.getBoolPref(kPrefCustomizationDebug);
-  } catch (ex) {}
+  gDebug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
   let consoleOptions = {
     maxLogLevel: gDebug ? "all" : "log",
     prefix: "CustomizeMode",
   };
   return new scope.ConsoleAPI(consoleOptions);
 });
 
 var gDisableAnimation = null;
@@ -619,21 +617,18 @@ CustomizeMode.prototype = {
     let deck = this.document.getElementById("tab-view-deck");
     let toolboxRect = this.window.gNavToolbox.getBoundingClientRect();
     let height = toolboxRect.bottom;
     deck.style.setProperty("--toolbox-rect-height", `${height}`);
     deck.style.setProperty("--toolbox-rect-height-with-unit", `${height}px`);
   },
 
   maybeShowTip(aAnchor) {
-    let shown = false;
     const kShownPref = "browser.customizemode.tip0.shown";
-    try {
-      shown = Services.prefs.getBoolPref(kShownPref);
-    } catch (ex) {}
+    let shown = Services.prefs.getBoolPref(kShownPref, false);
     if (shown)
       return;
 
     let anchorNode = aAnchor || this.document.getElementById("customization-panelHolder");
     let messageNode = this.tipPanel.querySelector(".customization-tipPanel-contentMessage");
     if (!messageNode.childElementCount) {
       // Put the tip contents in the popup.
       let bundle = this.document.getElementById("bundle_browser");
@@ -1497,20 +1492,17 @@ CustomizeMode.prototype = {
         break;
     }
   },
 
   _updateTitlebarButton() {
     if (!AppConstants.CAN_DRAW_IN_TITLEBAR) {
       return;
     }
-    let drawInTitlebar = true;
-    try {
-      drawInTitlebar = Services.prefs.getBoolPref(kDrawInTitlebarPref);
-    } catch (ex) { }
+    let drawInTitlebar = Services.prefs.getBoolPref(kDrawInTitlebarPref, true);
     let button = this.document.getElementById("customization-titlebar-visibility-button");
     // Drawing in the titlebar means 'hiding' the titlebar:
     if (drawInTitlebar) {
       button.removeAttribute("checked");
     } else {
       button.setAttribute("checked", "true");
     }
   },
--- a/browser/components/customizableui/test/browser_1042100_default_placements_update.js
+++ b/browser/components/customizableui/test/browser_1042100_default_placements_update.js
@@ -76,20 +76,17 @@ function test() {
     // versioned facility.  They're independent of kVersion and the saved
     // state's current version, so they may be present in the placements.
     for (let i = 0; i < placements.length; ) {
       if (placements[i] == testWidgetNew.id) {
         i++;
         continue;
       }
       let pref = "browser.toolbarbuttons.introduced." + placements[i];
-      let introduced = false;
-      try {
-        introduced = Services.prefs.getBoolPref(pref);
-      } catch (ex) {}
+      let introduced = Services.prefs.getBoolPref(pref, false);
       if (!introduced) {
         i++;
         continue;
       }
       placements.splice(i, 1);
     }
 
     is(placements.length, 1, "Should have 1 newly placed widget in nav-bar");
--- a/browser/components/distribution.js
+++ b/browser/components/distribution.js
@@ -17,20 +17,17 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 
 this.DistributionCustomizer = function DistributionCustomizer() {
   // For parallel xpcshell testing purposes allow loading the distribution.ini
   // file from the profile folder through an hidden pref.
-  let loadFromProfile = false;
-  try {
-    loadFromProfile = Services.prefs.getBoolPref("distribution.testing.loadFromProfile");
-  } catch (ex) {}
+  let loadFromProfile = Services.prefs.getBoolPref("distribution.testing.loadFromProfile", false);
   let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
                getService(Ci.nsIProperties);
   try {
     let iniFile = loadFromProfile ? dirSvc.get("ProfD", Ci.nsIFile)
                                   : dirSvc.get("XREAppDist", Ci.nsIFile);
     if (loadFromProfile) {
       iniFile.leafName = "distribution";
     }
@@ -55,22 +52,17 @@ DistributionCustomizer.prototype = {
       // Unable to parse INI.
       Cu.reportError("Unable to parse distribution.ini");
     }
     this.__defineGetter__("_ini", () => ini);
     return this._ini;
   },
 
   get _locale() {
-    let locale;
-    try {
-      locale = this._prefs.getCharPref("general.useragent.locale");
-    } catch (e) {
-      locale = "en-US";
-    }
+    let locale = this._prefs.getCharPref("general.useragent.locale", "en-US");
     this.__defineGetter__("_locale", () => locale);
     return this._locale;
   },
 
   get _language() {
     let language = this._locale.split("-")[0];
     this.__defineGetter__("_language", () => language);
     return this._language;
@@ -284,20 +276,17 @@ DistributionCustomizer.prototype = {
     try {
       bmProcessedPref = this._ini.getString("Global",
                                             "bookmarks.initialized.pref");
     } catch (e) {
       bmProcessedPref = "distribution." +
         this._ini.getString("Global", "id") + ".bookmarksProcessed";
     }
 
-    let bmProcessed = false;
-    try {
-      bmProcessed = this._prefs.getBoolPref(bmProcessedPref);
-    } catch (e) {}
+    let bmProcessed = this._prefs.getBoolPref(bmProcessedPref, false);
 
     if (!bmProcessed) {
       if (sections["BookmarksMenu"])
         yield this._parseBookmarksSection(PlacesUtils.bookmarks.menuGuid,
                                           "BookmarksMenu");
       if (sections["BookmarksToolbar"])
         yield this._parseBookmarksSection(PlacesUtils.bookmarks.toolbarGuid,
                                           "BookmarksToolbar");
--- a/browser/components/feeds/FeedWriter.js
+++ b/browser/components/feeds/FeedWriter.js
@@ -14,21 +14,17 @@ Cu.import("resource://gre/modules/NetUti
 
 const FEEDWRITER_CID = Components.ID("{49bb6593-3aff-4eb3-a068-2712c28bd58e}");
 const FEEDWRITER_CONTRACTID = "@mozilla.org/browser/feeds/result-writer;1";
 
 function LOG(str) {
   let prefB = Cc["@mozilla.org/preferences-service;1"].
               getService(Ci.nsIPrefBranch);
 
-  let shouldLog = false;
-  try {
-    shouldLog = prefB.getBoolPref("feeds.log");
-  } catch (ex) {
-  }
+  let shouldLog = prefB.getBoolPref("feeds.log", false);
 
   if (shouldLog)
     dump("*** Feeds: " + str + "\n");
 }
 
 /**
  * Wrapper function for nsIIOService::newURI.
  * @param aURLSpec
@@ -692,20 +688,17 @@ FeedWriter.prototype = {
 
   _getWebHandlerElementsForURL(aURL) {
     let menu = this._document.getElementById("handlersMenuList");
     return menu.querySelectorAll('[webhandlerurl="' + aURL + '"]');
   },
 
   _setSelectedHandler(feedType) {
     let prefs = Services.prefs;
-    let handler = "bookmarks";
-    try {
-      handler = prefs.getCharPref(getPrefReaderForType(feedType));
-    } catch (ex) { }
+    let handler = prefs.getCharPref(getPrefReaderForType(feedType), "bookmarks");
 
     switch (handler) {
       case "web": {
         if (this._handlersList) {
           let url;
           try {
             url = prefs.getComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString).data;
           } catch (ex) {
@@ -830,20 +823,17 @@ FeedWriter.prototype = {
     // in the list is changed
     handlersList.addEventListener("change", this);
 
     // Set up the "Subscribe Now" button
     this._document.getElementById("subscribeButton")
         .addEventListener("click", this);
 
     // first-run ui
-    let showFirstRunUI = true;
-    try {
-      showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
-    } catch (ex) { }
+    let showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI, true);
     if (showFirstRunUI) {
       let textfeedinfo1, textfeedinfo2;
       switch (feedType) {
         case Ci.nsIFeed.TYPE_VIDEO:
           textfeedinfo1 = "feedSubscriptionVideoPodcast1";
           textfeedinfo2 = "feedSubscriptionVideoPodcast2";
           break;
         case Ci.nsIFeed.TYPE_AUDIO:
--- a/browser/components/feeds/WebContentConverter.js
+++ b/browser/components/feeds/WebContentConverter.js
@@ -178,22 +178,19 @@ const Utils = {
       // This is handled internally, so we don't want them to register
       throw this.getSecurityError(
         `Permission denied to add ${aURIString} as a protocol handler`,
         aWindowOrNull);
     }
 
     // check if it is in the black list
     let pb = Services.prefs;
-    let allowed;
-    try {
-      allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "." + aProtocol);
-    } catch (e) {
-      allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "-default");
-    }
+    let allowed =
+      pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "." + aProtocol,
+                     pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "-default"));
     if (!allowed) {
       throw this.getSecurityError(
         `Not allowed to register a protocol handler for ${aProtocol}`,
         aWindowOrNull);
     }
   },
 
   // Return a SecurityError exception from the given Window if one is given.  If
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -93,30 +93,24 @@ const OVERRIDE_NEW_BUILD_ID = 3;
  *  OVERRIDE_NEW_PROFILE if this is the first run with a new profile.
  *  OVERRIDE_NEW_MSTONE if this is the first run with a build with a different
  *                      Gecko milestone (i.e. right after an upgrade).
  *  OVERRIDE_NEW_BUILD_ID if this is the first run with a new build ID of the
  *                        same Gecko milestone (i.e. after a nightly upgrade).
  *  OVERRIDE_NONE otherwise.
  */
 function needHomepageOverride(prefb) {
-  var savedmstone = null;
-  try {
-    savedmstone = prefb.getCharPref("browser.startup.homepage_override.mstone");
-  } catch (e) {}
+  var savedmstone = prefb.getCharPref("browser.startup.homepage_override.mstone", "");
 
   if (savedmstone == "ignore")
     return OVERRIDE_NONE;
 
   var mstone = Services.appinfo.platformVersion;
 
-  var savedBuildID = null;
-  try {
-    savedBuildID = prefb.getCharPref("browser.startup.homepage_override.buildID");
-  } catch (e) {}
+  var savedBuildID = prefb.getCharPref("browser.startup.homepage_override.buildID", "");
 
   var buildID = Services.appinfo.platformBuildID;
 
   if (mstone != savedmstone) {
     // Bug 462254. Previous releases had a default pref to suppress the EULA
     // agreement if the platform's installer had already shown one. Now with
     // about:rights we've removed the EULA stuff and default pref, but we need
     // a way to make existing profiles retain the default that we removed.
@@ -479,20 +473,17 @@ nsBrowserContentHandler.prototype = {
     var additionalPage = "";
     var willRestoreSession = false;
     try {
       // Read the old value of homepage_override.mstone before
       // needHomepageOverride updates it, so that we can later add it to the
       // URL if we do end up showing an overridePage. This makes it possible
       // to have the overridePage's content vary depending on the version we're
       // upgrading from.
-      let old_mstone = "unknown";
-      try {
-        old_mstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone");
-      } catch (ex) {}
+      let old_mstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone", "unknown");
       override = needHomepageOverride(prefb);
       if (override != OVERRIDE_NONE) {
         switch (override) {
           case OVERRIDE_NEW_PROFILE:
             // New profile.
             overridePage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url");
             additionalPage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url.additional");
             // Turn on 'later run' pages for new profiles.
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -930,20 +930,17 @@ BrowserGlue.prototype = {
       }
     });
 
     this._trackSlowStartup();
 
     // Offer to reset a user's profile if it hasn't been used for 60 days.
     const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000;
     let lastUse = Services.appinfo.replacedLockTime;
-    let disableResetPrompt = false;
-    try {
-      disableResetPrompt = Services.prefs.getBoolPref("browser.disableResetPrompt");
-    } catch (e) {}
+    let disableResetPrompt = Services.prefs.getBoolPref("browser.disableResetPrompt", false);
 
     if (!disableResetPrompt && lastUse &&
         Date.now() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS) {
       this._resetProfileNotification("unused");
     } else if (AppConstants.platform == "win" && !disableResetPrompt) {
       // Check if we were just re-installed and offer Firefox Reset
       let updateChannel;
       try {
@@ -1451,20 +1448,17 @@ BrowserGlue.prototype = {
     try {
       importBookmarksHTML =
         Services.prefs.getBoolPref("browser.places.importBookmarksHTML");
       if (importBookmarksHTML)
         importBookmarks = true;
     } catch (ex) {}
 
     // Support legacy bookmarks.html format for apps that depend on that format.
-    let autoExportHTML = false;
-    try {
-      autoExportHTML = Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML");
-    } catch (ex) {} // Do not export.
+    let autoExportHTML = Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML", false); // Do not export.
     if (autoExportHTML) {
       // Sqlite.jsm and Places shutdown happen at profile-before-change, thus,
       // to be on the safe side, this should run earlier.
       AsyncShutdown.profileChangeTeardown.addBlocker(
         "Places: export bookmarks.html",
         () => BookmarkHTMLUtils.exportToFile(BookmarkHTMLUtils.defaultPath));
     }
 
@@ -1521,20 +1515,17 @@ BrowserGlue.prototype = {
           yield this.ensurePlacesDefaultQueriesInitialized();
         } catch (e) {
           Cu.reportError(e);
         }
       } else {
         // An import operation is about to run.
         // Don't try to recreate smart bookmarks if autoExportHTML is true or
         // smart bookmarks are disabled.
-        let smartBookmarksVersion = 0;
-        try {
-          smartBookmarksVersion = Services.prefs.getIntPref("browser.places.smartBookmarksVersion");
-        } catch (ex) {}
+        let smartBookmarksVersion = Services.prefs.getIntPref("browser.places.smartBookmarksVersion", 0);
         if (!autoExportHTML && smartBookmarksVersion != -1)
           Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
 
         let bookmarksUrl = null;
         if (restoreDefaultBookmarks) {
           // User wants to restore bookmarks.html file from default profile folder
           bookmarksUrl = "chrome://browser/locale/bookmarks.html";
         } else if (yield OS.File.exists(BookmarkHTMLUtils.defaultPath)) {
@@ -1871,20 +1862,17 @@ BrowserGlue.prototype = {
         }
       } catch (ex) {}
     }
 
     if (currentUIVersion < 26) {
       // Refactor urlbar suggestion preferences to make it extendable and
       // allow new suggestion types (e.g: search suggestions).
       let types = ["history", "bookmark", "openpage"];
-      let defaultBehavior = 0;
-      try {
-        defaultBehavior = Services.prefs.getIntPref("browser.urlbar.default.behavior");
-      } catch (ex) {}
+      let defaultBehavior = Services.prefs.getIntPref("browser.urlbar.default.behavior", 0);
       try {
         let autocompleteEnabled = Services.prefs.getBoolPref("browser.urlbar.autocomplete.enabled");
         if (!autocompleteEnabled) {
           defaultBehavior = -1;
         }
       } catch (ex) {}
 
       // If the default behavior is:
@@ -2013,20 +2001,17 @@ BrowserGlue.prototype = {
 
     if (currentUIVersion < 42) {
       let backupFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
       backupFile.append("tabgroups-session-backup.json");
       OS.File.remove(backupFile.path, {ignoreAbsent: true}).catch(ex => Cu.reportError(ex));
     }
 
     if (currentUIVersion < 43) {
-      let currentTheme = null;
-      try {
-        currentTheme = Services.prefs.getCharPref("lightweightThemes.selectedThemeID");
-      } catch (e) {}
+      let currentTheme = Services.prefs.getCharPref("lightweightThemes.selectedThemeID", "");
       if (currentTheme == "firefox-devedition@mozilla.org") {
         let newTheme = Services.prefs.getCharPref("devtools.theme") == "dark" ?
           "firefox-compact-dark@mozilla.org" : "firefox-compact-light@mozilla.org";
         Services.prefs.setCharPref("lightweightThemes.selectedThemeID", newTheme);
       }
     }
 
     // Update the migration version.
@@ -2094,20 +2079,17 @@ BrowserGlue.prototype = {
     const SMART_BOOKMARKS_VERSION = 8;
     const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
     const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
 
     // TODO bug 399268: should this be a pref?
     const MAX_RESULTS = 10;
 
     // Get current smart bookmarks version.  If not set, create them.
-    let smartBookmarksCurrentVersion = 0;
-    try {
-      smartBookmarksCurrentVersion = Services.prefs.getIntPref(SMART_BOOKMARKS_PREF);
-    } catch (ex) {}
+    let smartBookmarksCurrentVersion = Services.prefs.getIntPref(SMART_BOOKMARKS_PREF, 0);
 
     // If version is current, or smart bookmarks are disabled, bail out.
     if (smartBookmarksCurrentVersion == -1 ||
         smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION) {
       return;
     }
 
     try {
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -69,32 +69,24 @@ var gSyncPane = {
 
     Services.obs.addObserver(onReady, "weave:service:ready", false);
     window.addEventListener("unload", onUnload);
 
     xps.ensureLoaded();
   },
 
   _showLoadPage(xps) {
-    let username;
-    try {
-      username = Services.prefs.getCharPref("services.sync.username");
-    } catch (e) {}
+    let username = Services.prefs.getCharPref("services.sync.username", "");
     if (!username) {
       this.page = FXA_PAGE_LOGGED_OUT;
       return;
     }
 
     // Use cached values while we wait for the up-to-date values
-    let cachedComputerName;
-    try {
-      cachedComputerName = Services.prefs.getCharPref("services.sync.client.name");
-    } catch (e) {
-      cachedComputerName = "";
-    }
+    let cachedComputerName = Services.prefs.getCharPref("services.sync.client.name", "");
     document.getElementById("fxaEmailAddress1").textContent = username;
     this._populateComputerName(cachedComputerName);
     this.page = FXA_PAGE_LOGGED_IN;
   },
 
   _init() {
     let topics = ["weave:service:login:error",
                   "weave:service:login:finish",
@@ -246,20 +238,17 @@ var gSyncPane = {
                   .getService(Components.interfaces.nsISupports)
                   .wrappedJSObject;
 
     let displayNameLabel = document.getElementById("fxaDisplayName");
     let fxaEmailAddress1Label = document.getElementById("fxaEmailAddress1");
     fxaEmailAddress1Label.hidden = false;
     displayNameLabel.hidden = true;
 
-    let profileInfoEnabled;
-    try {
-      profileInfoEnabled = Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled");
-    } catch (ex) {}
+    let profileInfoEnabled = Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled", false);
 
     // determine the fxa status...
     this._showLoadPage(service);
 
     fxAccounts.getSignedInUser().then(data => {
       if (!data) {
         this.page = FXA_PAGE_LOGGED_OUT;
         return false;
--- a/browser/components/privatebrowsing/test/browser/browser.ini
+++ b/browser/components/privatebrowsing/test/browser/browser.ini
@@ -21,17 +21,16 @@ support-files =
 [browser_privatebrowsing_DownloadLastDirWithCPS.js]
 [browser_privatebrowsing_about.js]
 tags = trackingprotection
 [browser_privatebrowsing_aboutHomeButtonAfterWindowClose.js]
 [browser_privatebrowsing_aboutSessionRestore.js]
 [browser_privatebrowsing_cache.js]
 [browser_privatebrowsing_certexceptionsui.js]
 [browser_privatebrowsing_concurrent.js]
-skip-if = e10s # Bug 1315042
 [browser_privatebrowsing_context_and_chromeFlags.js]
 [browser_privatebrowsing_crh.js]
 [browser_privatebrowsing_downloadLastDir.js]
 [browser_privatebrowsing_downloadLastDir_c.js]
 [browser_privatebrowsing_downloadLastDir_toggle.js]
 [browser_privatebrowsing_favicon.js]
 [browser_privatebrowsing_geoprompt.js]
 [browser_privatebrowsing_lastpbcontextexited.js]
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent.js
@@ -57,17 +57,17 @@ add_task(function* test() {
   // the private storage.
   private_window.close();
   private_window = yield BrowserTestUtils.openNewBrowserWindow({ private : false });
   private_browser = null;
   yield new Promise(resolve => Cu.schedulePreciseGC(resolve));
   private_browser = private_window.getBrowser().selectedBrowser;
 
   private_browser.loadURI(prefix + "?action=get&name=test2");
-  yield BrowserTestUtils.browserLoaded(private_browser);
+  yield BrowserTestUtils.browserLoaded(private_browser, false, prefix + "?action=get&name=test2");
   elts = yield getElts(private_browser);
   isnot(elts[0], "value2", "public window shouldn't see cleared private storage");
   is(elts[1], "1", "public window should only see public items");
 
 
   // Making it private again should clear the storage and it shouldn't
   // be able to see the old private storage as well.
   private_window.close();
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -1790,20 +1790,18 @@ this.UITour = {
       case "appinfo":
         let props = ["defaultUpdateChannel", "version"];
         let appinfo = {};
         props.forEach(property => appinfo[property] = Services.appinfo[property]);
 
         // Identifier of the partner repack, as stored in preference "distribution.id"
         // and included in Firefox and other update pings. Note this is not the same as
         // Services.appinfo.distributionID (value of MOZ_DISTRIBUTION_ID is set at build time).
-        let distribution = "default";
-        try {
-          distribution = Services.prefs.getDefaultBranch("distribution.").getCharPref("id");
-        } catch (e) {}
+        let distribution =
+          Services.prefs.getDefaultBranch("distribution.").getCharPref("id", "default");
         appinfo["distribution"] = distribution;
 
         let isDefaultBrowser = null;
         try {
           let shell = aWindow.getShellService();
           if (shell) {
             isDefaultBrowser = shell.isDefaultBrowser(false);
           }
--- a/browser/modules/BrowserUITelemetry.jsm
+++ b/browser/modules/BrowserUITelemetry.jsm
@@ -595,20 +595,17 @@ this.BrowserUITelemetry = {
     result.countableEvents = this._countableEvents;
     result.durations = this._durations;
     return result;
   },
 
   getSyncState() {
     let result = {};
     for (let sub of ["desktop", "mobile"]) {
-      let count = 0;
-      try {
-        count = Services.prefs.getIntPref("services.sync.clients.devices." + sub);
-      } catch (ex) {}
+      let count = Services.prefs.getIntPref("services.sync.clients.devices." + sub, 0);
       result[sub] = count;
     }
     return result;
   },
 
   countCustomizationEvent(aEventType) {
     this._countEvent(["customize", aEventType]);
   },
--- a/browser/modules/DirectoryLinksProvider.jsm
+++ b/browser/modules/DirectoryLinksProvider.jsm
@@ -183,20 +183,17 @@ var DirectoryLinksProvider = {
     return this.__linksURL;
   },
 
   /**
    * Gets the currently selected locale for display.
    * @return  the selected locale or "en-US" if none is selected
    */
   get locale() {
-    let matchOS;
-    try {
-      matchOS = Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE);
-    } catch (e) {}
+    let matchOS = Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE, false);
 
     if (matchOS) {
       return Cc["@mozilla.org/intl/ospreferences;1"].
              getService(Ci.mozIOSPreferences).systemLocale;
     }
 
     try {
       let locale = Services.prefs.getComplexValue(PREF_SELECTED_LOCALE,
--- a/browser/modules/SocialService.jsm
+++ b/browser/modules/SocialService.jsm
@@ -206,26 +206,21 @@ var ActiveProviders = {
                  createInstance(Ci.nsISupportsString);
     string.data = JSON.stringify(this._providers);
     Services.prefs.setComplexValue("social.activeProviders",
                                    Ci.nsISupportsString, string);
   }
 };
 
 function migrateSettings() {
-  let activeProviders, enabled;
-  try {
-    activeProviders = Services.prefs.getCharPref("social.activeProviders");
-  } catch (e) {
-    // not set, we'll check if we need to migrate older prefs
-  }
+  let enabled;
   if (Services.prefs.prefHasUserValue("social.enabled")) {
     enabled = Services.prefs.getBoolPref("social.enabled");
   }
-  if (activeProviders) {
+  if (Services.prefs.getCharPref("social.activeProviders", "")) {
     // migration from fx21 to fx22 or later
     // ensure any *builtin* provider in activeproviders is in user level prefs
     for (let origin in ActiveProviders._providers) {
       let prefname;
       let manifest;
       let defaultManifest;
       try {
         prefname = getPrefnameFromOrigin(origin);
@@ -279,20 +274,17 @@ function migrateSettings() {
       }
     }
     ActiveProviders.flush();
     Services.prefs.clearUserPref("social.enabled");
     return;
   }
 
   // primary migration from pre-fx21
-  let active;
-  try {
-    active = Services.prefs.getBoolPref("social.active");
-  } catch (e) {}
+  let active = Services.prefs.getBoolPref("social.active", false);
   if (!active)
     return;
 
   // primary difference from SocialServiceInternal.manifests is that we
   // only read the default branch here.
   let manifestPrefs = Services.prefs.getDefaultBranch("social.manifest.");
   let prefs = manifestPrefs.getChildList("", []);
   for (let pref of prefs) {
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -188,22 +188,17 @@ DevTools.prototype = {
   getToolDefinition: function DT_getToolDefinition(toolId) {
     let tool = this._tools.get(toolId);
     if (!tool) {
       return null;
     } else if (!tool.visibilityswitch) {
       return tool;
     }
 
-    let enabled;
-    try {
-      enabled = Services.prefs.getBoolPref(tool.visibilityswitch);
-    } catch (e) {
-      enabled = true;
-    }
+    let enabled = Services.prefs.getBoolPref(tool.visibilityswitch, true);
 
     return enabled ? tool : null;
   },
 
   /**
    * Allow ToolBoxes to get at the list of tools that they should populate
    * themselves with.
    *
--- a/devtools/client/framework/toolbox-process-window.js
+++ b/devtools/client/framework/toolbox-process-window.js
@@ -95,27 +95,23 @@ function openToolbox({ form, chrome, isT
   let options = {
     form: form,
     client: gClient,
     chrome: chrome,
     isTabActor: isTabActor
   };
   TargetFactory.forRemoteTab(options).then(target => {
     let frame = document.getElementById("toolbox-iframe");
-    let selectedTool = "jsdebugger";
 
-    try {
-      // Remember the last panel that was used inside of this profile.
-      selectedTool = Services.prefs.getCharPref("devtools.toolbox.selectedTool");
-    } catch(e) {}
-
-    try {
-      // But if we are testing, then it should always open the debugger panel.
-      selectedTool = Services.prefs.getCharPref("devtools.browsertoolbox.panel");
-    } catch(e) {}
+    // Remember the last panel that was used inside of this profile.
+    // But if we are testing, then it should always open the debugger panel.
+    let selectedTool =
+      Services.prefs.getCharPref("devtools.browsertoolbox.panel",
+                                 Services.prefs.getCharPref("devtools.toolbox.selectedTool",
+                                                            "jsdebugger"));
 
     let options = { customIframe: frame };
     gDevTools.showToolbox(target,
                           selectedTool,
                           Toolbox.HostType.CUSTOM,
                           options)
              .then(onNewToolbox);
   });
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -1221,22 +1221,17 @@ Toolbox.prototype = {
    * Ensure the visibility of each toolbox button matches the preference value.
    */
   _commandIsVisible: function (button) {
     const {
       isTargetSupported,
       visibilityswitch
     } = button;
 
-    let visible = true;
-    try {
-      visible = Services.prefs.getBoolPref(visibilityswitch);
-    } catch (ex) {
-      // Do nothing.
-    }
+    let visible = Services.prefs.getBoolPref(visibilityswitch, true);
 
     if (isTargetSupported) {
       return visible && isTargetSupported(this.target);
     }
 
     return visible;
   },
 
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -66,21 +66,18 @@ const ATTR_COLLAPSE_LENGTH_PREF = "devto
 function MarkupView(inspector, frame, controllerWindow) {
   this.inspector = inspector;
   this.walker = this.inspector.walker;
   this._frame = frame;
   this.win = this._frame.contentWindow;
   this.doc = this._frame.contentDocument;
   this._elt = this.doc.querySelector("#root");
 
-  try {
-    this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize");
-  } catch (ex) {
-    this.maxChildren = DEFAULT_MAX_CHILDREN;
-  }
+  this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize",
+                                               DEFAULT_MAX_CHILDREN);
 
   this.collapseAttributes =
     Services.prefs.getBoolPref(ATTR_COLLAPSE_ENABLED_PREF);
   this.collapseAttributeLength =
     Services.prefs.getIntPref(ATTR_COLLAPSE_LENGTH_PREF);
 
   // Creating the popup to be used to show CSS suggestions.
   // The popup will be attached to the toolbox document.
--- a/devtools/shared/touch/simulator-core.js
+++ b/devtools/shared/touch/simulator-core.js
@@ -16,29 +16,18 @@ var systemAppOrigin = (function () {
       Services.io.newURI(Services.prefs.getCharPref("b2g.system_manifest_url"))
                  .prePath;
   } catch (e) {
     // Fall back to default value
   }
   return systemOrigin;
 })();
 
-var threshold = 25;
-try {
-  threshold = Services.prefs.getIntPref("ui.dragThresholdX");
-} catch (e) {
-  // Fall back to default value
-}
-
-var delay = 500;
-try {
-  delay = Services.prefs.getIntPref("ui.click_hold_context_menus.delay");
-} catch (e) {
-  // Fall back to default value
-}
+var threshold = Services.prefs.getIntPref("ui.dragThresholdX", 25);
+var delay = Services.prefs.getIntPref("ui.click_hold_context_menus.delay", 500);
 
 function SimulatorCore(simulatorTarget) {
   this.simulatorTarget = simulatorTarget;
 }
 
 /**
  * Simulate touch events for platforms where they aren't generally available.
  */
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10380,30 +10380,33 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       nsCOMPtr<nsIDocument> doc = GetDocument();
       NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
       doc->SetDocumentURI(aURI);
 
       /* This is a anchor traversal with in the same page.
        * call OnNewURI() so that, this traversal will be
        * recorded in session and global history.
        */
-      nsCOMPtr<nsIPrincipal> triggeringPrincipal, principalToInherit;
+      nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit;
       if (mOSHE) {
-        mOSHE->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
-        mOSHE->GetPrincipalToInherit(getter_AddRefs(principalToInherit));
+        mOSHE->GetTriggeringPrincipal(getter_AddRefs(newURITriggeringPrincipal));
+        mOSHE->GetPrincipalToInherit(getter_AddRefs(newURIPrincipalToInherit));
+      } else {
+        newURITriggeringPrincipal = aTriggeringPrincipal;
+        newURIPrincipalToInherit = doc->NodePrincipal();
       }
       // Pass true for aCloneSHChildren, since we're not
       // changing documents here, so all of our subframes are
       // still relevant to the new session history entry.
       //
       // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
       // flag on firing onLocationChange(...).
       // Anyway, aCloneSHChildren param is simply reflecting
       // doShortCircuitedLoad in this scope.
-      OnNewURI(aURI, nullptr, triggeringPrincipal, principalToInherit,
+      OnNewURI(aURI, nullptr, newURITriggeringPrincipal, newURIPrincipalToInherit,
                mLoadType, true, true, true);
 
       nsCOMPtr<nsIInputStream> postData;
       nsCOMPtr<nsISupports> cacheKey;
 
       bool scrollRestorationIsManual = false;
       if (mOSHE) {
         /* save current position of scroller(s) (bug 59774) */
new file mode 100644
--- /dev/null
+++ b/docshell/test/dummy_page.html
@@ -0,0 +1,6 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+  <body>
+    just a dummy html file
+  </body>
+</html>
--- a/docshell/test/mochitest.ini
+++ b/docshell/test/mochitest.ini
@@ -6,16 +6,17 @@ support-files =
   bug404548-subframe.html
   bug413310-post.sjs
   bug413310-subframe.html
   bug529119-window.html
   bug570341_recordevents.html
   bug668513_redirect.html
   bug668513_redirect.html^headers^
   bug691547_frame.html
+  dummy_page.html
   file_anchor_scroll_after_document_open.html
   file_bug385434_1.html
   file_bug385434_2.html
   file_bug385434_3.html
   file_bug475636.sjs
   file_bug509055.html
   file_bug540462.html
   file_bug580069_1.html
@@ -87,8 +88,9 @@ support-files = file_bug668513.html
 [test_bug1121701.html]
 [test_bug1186774.html]
 [test_forceinheritprincipal_overrule_owner.html]
 [test_framedhistoryframes.html]
 skip-if = toolkit == 'android' # bug 784321
 support-files = file_framedhistoryframes.html
 [test_pushState_after_document_open.html]
 [test_windowedhistoryframes.html]
+[test_triggeringprincipal_location_seturi.html]
new file mode 100644
--- /dev/null
+++ b/docshell/test/test_triggeringprincipal_location_seturi.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+const SAME_ORIGIN_URI = "http://mochi.test:8888/tests/docshell/test/dummy_page.html";
+const CROSS_ORIGIN_URI = "http://example.com/tests/docshell/test/dummy_page.html";
+const NUMBER_OF_TESTS = 3;
+let testCounter = 0;
+
+function checkFinish() {
+  testCounter++;
+  if (testCounter < NUMBER_OF_TESTS) {
+  	return;
+  }
+  SimpleTest.finish();
+}
+
+// ---- test 1 ----
+
+let myFrame1 = document.createElement("iframe");
+myFrame1.src = SAME_ORIGIN_URI;
+myFrame1.addEventListener("load", checkLoadFrame1);
+document.documentElement.appendChild(myFrame1);
+
+function checkLoadFrame1() {
+  myFrame1.removeEventListener('load', checkLoadFrame1, false);
+  // window.location.href is no longer cross-origin accessible in gecko.
+  is(SpecialPowers.wrap(myFrame1.contentWindow).location.href, SAME_ORIGIN_URI,
+    "initial same origin dummy loaded into frame1");
+
+  SpecialPowers.wrap(myFrame1.contentWindow).location.hash = "#bar";
+  is(SpecialPowers.wrap(myFrame1.contentWindow).location.href, SAME_ORIGIN_URI + "#bar",
+    "initial same origin dummy#bar loaded into iframe1");
+
+  myFrame1.addEventListener("load", checkNavFrame1);
+  myFrame1.src = CROSS_ORIGIN_URI;
+}
+
+function checkNavFrame1() {
+  myFrame1.removeEventListener('load', checkNavFrame1, false);
+  is(SpecialPowers.wrap(myFrame1.contentWindow).location.href, CROSS_ORIGIN_URI,
+    "cross origin dummy loaded into frame1");
+
+  myFrame1.addEventListener("load", checkBackNavFrame1);
+  myFrame1.src = SAME_ORIGIN_URI + "#bar";
+}
+
+function checkBackNavFrame1() {
+  myFrame1.removeEventListener('load', checkBackNavFrame1, false);
+  is(SpecialPowers.wrap(myFrame1.contentWindow).location.href, SAME_ORIGIN_URI + "#bar",
+    "navagiating back to same origin dummy for frame1");
+  checkFinish();
+}
+
+// ---- test 2 ----
+
+let myFrame2 = document.createElement("iframe");
+myFrame2.src = "about:blank";
+myFrame2.addEventListener("load", checkLoadFrame2);
+document.documentElement.appendChild(myFrame2);
+
+function checkLoadFrame2() {
+  myFrame2.removeEventListener('load', checkLoadFrame2, false);
+  is(SpecialPowers.wrap(myFrame2.contentWindow).location.href, "about:blank",
+    "initial about:blank frame loaded");
+
+  myFrame2.contentWindow.location.hash = "#foo";
+  is(SpecialPowers.wrap(myFrame2.contentWindow).location.href, "about:blank#foo",
+      "about:blank#foo frame loaded");
+  
+  myFrame2.addEventListener('load', checkHistoryFrame2);
+  myFrame2.src = "about:blank";
+}
+
+function checkHistoryFrame2() {
+  myFrame2.removeEventListener('load', checkHistoryFrame2, false);
+  is(SpecialPowers.wrap(myFrame2.contentWindow).location.href, "about:blank",
+    "about:blank frame loaded again");
+  checkFinish();
+}
+
+// ---- test 3 ----
+
+let myFrame3 = document.createElement("frame");
+document.documentElement.appendChild(myFrame3);
+myFrame3.contentWindow.location.hash = "#foo";
+
+is(myFrame3.contentWindow.location.href, "about:blank#foo",
+   "created history entry with about:blank#foo");
+checkFinish();
+
+</script>
+</body>
+</html>
--- a/dom/base/ProcessSelector.js
+++ b/dom/base/ProcessSelector.js
@@ -7,30 +7,20 @@ const { classes: Cc, interfaces: Ci, uti
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import('resource://gre/modules/Services.jsm');
 
 const BASE_PREF = "dom.ipc.processCount"
 const PREF_BRANCH = BASE_PREF + ".";
 
 // Utilities:
 function getMaxContentParents(processType) {
-  let maxContentParents = -1;
-  try {
-    maxContentParents = Services.prefs.getIntPref(PREF_BRANCH + processType);
-  } catch (e) {
-    // Pref probably didn't exist, get the default number of processes.
-    try {
-      maxContentParents = Services.prefs.getIntPref(BASE_PREF);
-    } catch (e) {
-      // No prefs? That's odd, use only one process.
-      maxContentParents = 1;
-    }
-  }
-
-  return maxContentParents;
+  // If the pref doesn't exist, get the default number of processes.
+  // If there's no pref, use only one process.
+  return Services.prefs.getIntPref(PREF_BRANCH + processType,
+                                   Services.prefs.getIntPref(BASE_PREF, 1));
 }
 
 // Fills up aProcesses until max and then selects randomly from the available
 // ones.
 function RandomSelector() {
 }
 
 RandomSelector.prototype = {
@@ -53,10 +43,41 @@ RandomSelector.prototype = {
 
       curIdx = (curIdx + 1) % maxContentParents;
     } while (curIdx !== startIdx);
 
     return Ci.nsIContentProcessProvider.NEW_PROCESS;
   },
 };
 
-var components = [RandomSelector];
+// Fills up aProcesses until max and then selects one from the available
+// ones that host the least number of tabs.
+function MinTabSelector() {
+}
+
+MinTabSelector.prototype = {
+  classID:          Components.ID("{2dc08eaf-6eef-4394-b1df-a3a927c1290b}"),
+  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIContentProcessProvider]),
+
+  provideProcess(aType, aOpener, aProcesses, aCount) {
+    let maxContentParents = getMaxContentParents(aType);
+    if (aCount < maxContentParents) {
+      return Ci.nsIContentProcessProvider.NEW_PROCESS;
+    }
+
+    let min = Number.MAX_VALUE;
+    let candidate = Ci.nsIContentProcessProvider.NEW_PROCESS;
+
+    for (let i = 0; i < maxContentParents; i++) {
+      let process = aProcesses[i];
+      let tabCount = process.tabCount;
+      if (process.opener === aOpener && tabCount < min) {
+        min = tabCount;
+        candidate = i;
+      }
+    }
+
+    return candidate;
+  },
+};
+
+var components = [RandomSelector, MinTabSelector];
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
--- a/dom/base/ProcessSelector.manifest
+++ b/dom/base/ProcessSelector.manifest
@@ -1,2 +1,3 @@
 component {c616fcfd-9737-41f1-aa74-cee72a38f91b} ProcessSelector.js
-contract @mozilla.org/ipc/processselector;1 {c616fcfd-9737-41f1-aa74-cee72a38f91b}
+component {2dc08eaf-6eef-4394-b1df-a3a927c1290b} ProcessSelector.js
+contract @mozilla.org/ipc/processselector;1 {2dc08eaf-6eef-4394-b1df-a3a927c1290b}
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2403,18 +2403,23 @@ nsGlobalWindow::WouldReuseInnerWindow(ns
   if (!mDoc || !aNewDocument) {
     return false;
   }
 
   if (!mDoc->IsInitialDocument()) {
     return false;
   }
 
-  NS_ASSERTION(NS_IsAboutBlank(mDoc->GetDocumentURI()),
-               "How'd this happen?");
+#ifdef DEBUG
+{
+  nsCOMPtr<nsIURI> uri;
+  mDoc->GetDocumentURI()->CloneIgnoringRef(getter_AddRefs(uri));
+  NS_ASSERTION(NS_IsAboutBlank(uri), "How'd this happen?");
+}
+#endif
 
   // Great, we're the original document, check for one of the other
   // conditions.
 
   if (mDoc == aNewDocument) {
     return true;
   }
 
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -959,21 +959,17 @@ BrowserElementChild.prototype = {
     let maxHeight = data.json.args.height;
     let mimeType = data.json.args.mimeType;
     let domRequestID = data.json.id;
 
     let takeScreenshotClosure = function() {
       self._takeScreenshot(maxWidth, maxHeight, mimeType, domRequestID);
     };
 
-    let maxDelayMS = 2000;
-    try {
-      maxDelayMS = Services.prefs.getIntPref('dom.browserElement.maxScreenshotDelayMS');
-    }
-    catch(e) {}
+    let maxDelayMS = Services.prefs.getIntPref('dom.browserElement.maxScreenshotDelayMS', 2000);
 
     // Try to wait for the event loop to go idle before we take the screenshot,
     // but once we've waited maxDelayMS milliseconds, go ahead and take it
     // anyway.
     Cc['@mozilla.org/message-loop;1'].getService(Ci.nsIMessageLoop).postIdleTask(
       takeScreenshotClosure, maxDelayMS);
   },
 
@@ -1644,20 +1640,17 @@ BrowserElementChild.prototype = {
               let nssErrorsService = Cc['@mozilla.org/nss_errors_service;1']
                                        .getService(Ci.nsINSSErrorsService);
               if (nssErrorsService.getErrorClass(status)
                     == Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT) {
                 // XXX Is there a point firing the event if the error page is not
                 // certerror? If yes, maybe we should add a property to the
                 // event to to indicate whether there is a custom page. That would
                 // let the embedder have more control over the desired behavior.
-                let errorPage = null;
-                try {
-                  errorPage = Services.prefs.getCharPref(CERTIFICATE_ERROR_PAGE_PREF);
-                } catch (e) {}
+                let errorPage = Services.prefs.getCharPref(CERTIFICATE_ERROR_PAGE_PREF, "");
 
                 if (errorPage == 'certerror') {
                   sendAsyncMsg('error', { type: 'certerror' });
                   return;
                 }
               }
             } catch (e) {}
 
--- a/dom/indexedDB/test/test_globalObjects_other.xul
+++ b/dom/indexedDB/test/test_globalObjects_other.xul
@@ -26,24 +26,18 @@
     AddonManager.getAddonByID("indexedDB-test@mozilla.org",
                               grabEventAndContinueHandler);
     let addon = yield undefined;
     addon.uninstall();
 
     Cu.import("resource://gre/modules/Services.jsm");
     for (var stage of [ "install", "startup", "shutdown", "uninstall" ]) {
       for (var symbol of [ "IDBKeyRange", "indexedDB" ]) {
-        let pref;
-        try {
-          pref = Services.prefs.getBoolPref("indexeddbtest.bootstrap." + stage +
-                                            "." + symbol);
-        }
-        catch(ex) {
-          pref = false;
-        }
+        let pref = Services.prefs.getBoolPref("indexeddbtest.bootstrap." + stage +
+                                              "." + symbol, false);
         ok(pref, "Symbol '" + symbol + "' present during '" + stage + "'");
       }
     }
 
     finishTest();
     yield undefined;
   }
 
--- a/dom/interfaces/base/nsIContentProcess.idl
+++ b/dom/interfaces/base/nsIContentProcess.idl
@@ -23,16 +23,21 @@ interface nsIContentProcessInfo : nsISup
   readonly attribute int32_t processId;
 
   /**
    * This content process's opener.
    */
   readonly attribute nsIContentProcessInfo opener;
 
   /**
+   * Number of opened tabs living in this content process.
+   */
+  readonly attribute int32_t tabCount;
+
+  /**
    * The process manager for this ContentParent (so a process message manager
    * as opposed to a frame message manager.
    */
   readonly attribute nsIMessageSender messageManager;
 };
 
 [scriptable, uuid(83ffb063-5f65-4c45-ae07-3f553e0809bb)]
 interface nsIContentProcessProvider : nsISupports
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -598,20 +598,16 @@ ContentChild::Init(MessageLoop* aIOLoop,
 #ifdef NS_PRINTING
   // Force the creation of the nsPrintingProxy so that it's IPC counterpart,
   // PrintingParent, is always available for printing initiated from the parent.
   RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
 #endif
 
   SetProcessName(NS_LITERAL_STRING("Web Content"), true);
 
-  nsTArray<mozilla::dom::GfxInfoFeatureStatus> featureStatus;
-  SendGetGfxInfoFeatureStatus(&featureStatus);
-  GfxInfoBase::SetFeatureStatus(featureStatus);
-
   return true;
 }
 
 void
 ContentChild::SetProcessName(const nsAString& aName, bool aDontOverride)
 {
   if (!mCanOverrideProcessName) {
     return;
@@ -1028,16 +1024,18 @@ ContentChild::InitXPCOM(const XPCOMInitD
   }
 
   // The stylesheet cache is not ready yet. Store this URL for future use.
   nsCOMPtr<nsIURI> ucsURL = DeserializeURI(aXPCOMInit.userContentSheetURL());
   nsLayoutStylesheetCache::SetUserContentCSSURL(ucsURL);
 
   // This will register cross-process observer.
   mozilla::dom::time::InitializeDateCacheCleaner();
+
+  GfxInfoBase::SetFeatureStatus(aXPCOMInit.gfxFeatureStatus());
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvRequestMemoryReport(const uint32_t& aGeneration,
                                       const bool& aAnonymize,
                                       const bool& aMinimizeMemoryUsage,
                                       const MaybeFileDesc& aDMDFile)
 {
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -501,16 +501,29 @@ ScriptableCPInfo::GetOpener(nsIContentPr
   if (ContentParent* opener = mContentParent->Opener()) {
     nsCOMPtr<nsIContentProcessInfo> info = opener->ScriptableHelper();
     info.forget(aInfo);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+ScriptableCPInfo::GetTabCount(int32_t* aTabCount)
+{
+  if (!mContentParent) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  *aTabCount = cpm->GetTabParentCountByProcessId(mContentParent->ChildID());
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 ScriptableCPInfo::GetMessageManager(nsIMessageSender** aMessenger)
 {
   *aMessenger = nullptr;
   if (!mContentParent) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   nsCOMPtr<nsIMessageSender> manager = mContentParent->GetMessageManager();
@@ -752,33 +765,38 @@ ContentParent::ReleaseCachedProcesses()
     // Make sure we don't select this process for new tabs.
     cp->MarkAsDead();
     // Make sure that this process is no longer accessible from JS by its message manager.
     cp->ShutDownMessageManager();
   }
 }
 
 /*static*/ already_AddRefed<ContentParent>
-ContentParent::RandomSelect(const nsTArray<ContentParent*>& aContentParents,
+ContentParent::MinTabSelect(const nsTArray<ContentParent*>& aContentParents,
                             ContentParent* aOpener, int32_t aMaxContentParents)
 {
   uint32_t maxSelectable = std::min(static_cast<uint32_t>(aContentParents.Length()),
                                     static_cast<uint32_t>(aMaxContentParents));
-  uint32_t startIdx = rand() % maxSelectable;
-  uint32_t currIdx = startIdx;
-  do {
-    RefPtr<ContentParent> p = aContentParents[currIdx];
+  uint32_t min = INT_MAX;
+  RefPtr<ContentParent> candidate;
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+
+  for (uint32_t i = 0; i < maxSelectable; i++) {
+    ContentParent* p = aContentParents[i];
     NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sBrowserContentParents?");
     if (p->mOpener == aOpener) {
-      return p.forget();
+      uint32_t tabCount = cpm->GetTabParentCountByProcessId(p->ChildID());
+      if (tabCount < min) {
+        candidate = p;
+        min = tabCount;
+      }
     }
-    currIdx = (currIdx + 1) % maxSelectable;
-  } while (currIdx != startIdx);
-
-  return nullptr;
+  }
+
+  return candidate.forget();
 }
 
 /*static*/ already_AddRefed<ContentParent>
 ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
                                           ProcessPriority aPriority,
                                           ContentParent* aOpener)
 {
   nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
@@ -811,17 +829,17 @@ ContentParent::GetNewOrUsedBrowserProces
         return retval.forget();
       }
     } else {
       // If there was a problem with the JS chooser, fall back to a random
       // selection.
       NS_WARNING("nsIContentProcessProvider failed to return a process");
       RefPtr<ContentParent> random;
       if (contentParents.Length() >= maxContentParents &&
-          (random = RandomSelect(contentParents, aOpener, maxContentParents))) {
+          (random = MinTabSelect(contentParents, aOpener, maxContentParents))) {
         return random.forget();
       }
     }
 
     // Try to take the preallocated process only for the default process type.
     RefPtr<ContentParent> p;
     if (aRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) &&
         (p = PreallocatedProcessManager::Take())) {
@@ -2169,16 +2187,30 @@ ContentParent::InitInternal(ProcessPrior
   // send the file URL instead.
   StyleSheet* ucs = nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
   if (ucs) {
     SerializeURI(ucs->GetSheetURI(), xpcomInit.userContentSheetURL());
   } else {
     SerializeURI(nullptr, xpcomInit.userContentSheetURL());
   }
 
+  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+  if (gfxInfo) {
+    for (int32_t i = 1; i <= nsIGfxInfo::FEATURE_MAX_VALUE; ++i) {
+      int32_t status = 0;
+      nsAutoCString failureId;
+      gfxInfo->GetFeatureStatus(i, failureId, &status);
+      dom::GfxInfoFeatureStatus gfxFeatureStatus;
+      gfxFeatureStatus.feature() = i;
+      gfxFeatureStatus.status() = status;
+      gfxFeatureStatus.failureId() = failureId;
+      xpcomInit.gfxFeatureStatus().AppendElement(gfxFeatureStatus);
+    }
+  }
+
   Unused << SendSetXPCOMProcessAttributes(xpcomInit, initialData, lnfCache);
 
   if (aSendRegisteredChrome) {
     nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
     nsChromeRegistryChrome* chromeRegistry =
       static_cast<nsChromeRegistryChrome*>(registrySvc.get());
     chromeRegistry->SendRegisteredChrome(this);
   }
@@ -4013,35 +4045,16 @@ ContentParent::RecvRecordingDeviceEvents
                          aRecordingStatus.get());
   } else {
     NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents.");
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentParent::RecvGetGfxInfoFeatureStatus(nsTArray<mozilla::dom::GfxInfoFeatureStatus>* aFS)
-{
-  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
-  if (!gfxInfo) {
-    return IPC_OK();
-  }
-
-  for (int32_t i = 1; i <= nsIGfxInfo::FEATURE_MAX_VALUE; ++i) {
-    int32_t status = 0;
-    nsAutoCString failureId;
-    gfxInfo->GetFeatureStatus(i, failureId, &status);
-    mozilla::dom::GfxInfoFeatureStatus fs(i, status, failureId);
-    aFS->AppendElement(Move(fs));
-  }
-
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
 ContentParent::RecvAddIdleObserver(const uint64_t& aObserver,
                                    const uint32_t& aIdleTimeInS)
 {
   nsresult rv;
   nsCOMPtr<nsIIdleService> idleService =
     do_GetService("@mozilla.org/widget/idleservice;1", &rv);
   NS_ENSURE_SUCCESS(rv, IPC_FAIL_NO_REASON(this));
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -154,17 +154,17 @@ public:
   static void ReleaseCachedProcesses();
 
   /**
    * Picks a random content parent from |aContentParents| with a given |aOpener|
    * respecting the index limit set by |aMaxContentParents|.
    * Returns null if non available.
    */
   static already_AddRefed<ContentParent>
-  RandomSelect(const nsTArray<ContentParent*>& aContentParents,
+  MinTabSelect(const nsTArray<ContentParent*>& aContentParents,
                ContentParent* aOpener,
                int32_t maxContentParents);
 
   /**
    * Get or create a content process for:
    * 1. browser iframe
    * 2. remote xul <browser>
    * 3. normal iframe
@@ -464,18 +464,16 @@ public:
 
   virtual PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent() override;
 
   virtual mozilla::ipc::IPCResult RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
                                                             const nsString& aPageURL,
                                                             const bool& aIsAudio,
                                                             const bool& aIsVideo) override;
 
-  virtual mozilla::ipc::IPCResult RecvGetGfxInfoFeatureStatus(nsTArray<mozilla::dom::GfxInfoFeatureStatus>* aFS) override;
-
   bool CycleCollectWithLogs(bool aDumpAllTraces,
                             nsICycleCollectorLogSink* aSink,
                             nsIDumpGCAndCCLogsCallback* aCallback);
 
   virtual PBlobParent*
   SendPBlobConstructor(PBlobParent* aActor,
                        const BlobConstructorParams& aParams) override;
 
--- a/dom/ipc/ContentProcessManager.cpp
+++ b/dom/ipc/ContentProcessManager.cpp
@@ -349,10 +349,23 @@ ContentProcessManager::GetTabParentsByPr
       remoteFrameIter != iter->second.mRemoteFrames.end();
       ++remoteFrameIter) {
     tabIdList.AppendElement(remoteFrameIter->first);
   }
 
   return Move(tabIdList);
 }
 
+uint32_t
+ContentProcessManager::GetTabParentCountByProcessId(const ContentParentId& aChildCpId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto iter = mContentParentMap.find(aChildCpId);
+  if (NS_WARN_IF(iter == mContentParentMap.end())) {
+    return 0;
+  }
+
+  return iter->second.mRemoteFrames.size();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentProcessManager.h
+++ b/dom/ipc/ContentProcessManager.h
@@ -113,16 +113,23 @@ public:
   /**
    * Get all TabParents' Ids managed by the givent content process.
    * Return empty array when TabParent couldn't be found via aChildCpId
    */
   nsTArray<TabId>
   GetTabParentsByProcessId(const ContentParentId& aChildCpId);
 
   /**
+   * Get the number of TabParents managed by the givent content process.
+   * Return 0 when TabParent couldn't be found via aChildCpId.
+   */
+  uint32_t
+  GetTabParentCountByProcessId(const ContentParentId& aChildCpId);
+
+  /**
    * Get the TabParent by the given content process and tab id.
    * Return nullptr when TabParent couldn't be found via aChildCpId
    * and aChildTabId.
    * (or probably because the TabParent is not in the chrome process)
    */
   already_AddRefed<TabParent>
   GetTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
                                 const TabId& aChildTabId);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -345,37 +345,38 @@ struct GMPAPITags
 
 struct GMPCapabilityData
 {
     nsCString name;
     nsCString version;
     GMPAPITags[] capabilities;
 };
 
+struct GfxInfoFeatureStatus
+{
+    int32_t feature;
+    int32_t status;
+    nsCString failureId;
+};
+
 struct XPCOMInitData
 {
     bool isOffline;
     bool isConnected;
     int32_t captivePortalState;
     bool isLangRTL;
     bool haveBidiKeyboards;
     nsString[] dictionaries;
     ClipboardCapabilities clipboardCaps;
     DomainPolicyClone domainPolicy;
     /* used on MacOSX only */
     FontFamilyListEntry[] fontFamilies;
     OptionalURIParams userContentSheetURL;
     PrefSetting[] prefs;
-};
-
-struct GfxInfoFeatureStatus
-{
-    int32_t feature;
-    int32_t status;
-    nsCString failureId;
+    GfxInfoFeatureStatus[] gfxFeatureStatus;
 };
 
 /**
  * The PContent protocol is a top-level protocol between the UI process
  * and a content process. There is exactly one PContentParent/PContentChild pair
  * for each content process.
  */
 nested(upto inside_cpow) sync protocol PContent
@@ -959,18 +960,16 @@ parent:
      * @param isAudio recording start with microphone
      * @param isVideo recording start with camera
      */
     async RecordingDeviceEvents(nsString recordingStatus,
                                 nsString pageURL,
                                 bool isAudio,
                                 bool isVideo);
 
-    sync GetGfxInfoFeatureStatus() returns (GfxInfoFeatureStatus[] features);
-
     // Graphics errors
     async GraphicsError(nsCString aError);
 
     // Driver crash guards. aGuardType must be a member of CrashGuardType.
     sync BeginDriverCrashGuard(uint32_t aGuardType) returns (bool crashDetected);
     sync EndDriverCrashGuard(uint32_t aGuardType);
 
     async AddIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
--- a/dom/media/webaudio/test/test_audioContextSuspendResumeClose.html
+++ b/dom/media/webaudio/test/test_audioContextSuspendResumeClose.html
@@ -43,22 +43,20 @@ function tryToCreateNodeOnClosedContext(
 
       expectException(function() {
         ctx[e.name].apply(ctx, e.args);
       }, DOMException.INVALID_STATE_ERR);
     });
 }
 
 function loadFile(url, callback) {
-  todo(false, "loadFile: " + url);
   var xhr = new XMLHttpRequest();
   xhr.open("GET", url, true);
   xhr.responseType = "arraybuffer";
   xhr.onload = function() {
-    todo(false, "loadFile: " + url + " calling callback...");
     callback(xhr.response);
   };
   xhr.send();
 }
 
 // createBuffer, createPeriodicWave and decodeAudioData should work on a context
 // that has `state` == "closed"
 function tryLegalOpeerationsOnClosedContext(ctx) {
@@ -388,23 +386,28 @@ function finish() {
     SimpleTest.finish();
   }
 }
 
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   var tests = [
-    testAudioContext,
     testOfflineAudioContext,
     testScriptProcessNodeSuspended,
     testMultiContextOutput,
     testMultiContextInput,
     testSuspendResumeEventLoop
   ];
+
+  // See Bug 1305136, many intermittent failures on Linux
+  if (!navigator.platform.startsWith("Linux")) {
+    tests.push(testAudioContext);
+  }
+
   remaining = tests.length;
   tests.forEach(function(f) { f() });
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/presentation/PresentationDataChannelSessionTransport.js
+++ b/dom/presentation/PresentationDataChannelSessionTransport.js
@@ -104,23 +104,17 @@ PresentationTransportBuilder.prototype =
         }
         break;
       default:
        throw Cr.NS_ERROR_ILLEGAL_VALUE;
     }
 
     // TODO bug 1228235 we should have a way to let device providers customize
     // the time-out duration.
-    let timeout;
-    try {
-      timeout = Services.prefs.getIntPref("presentation.receiver.loading.timeout");
-    } catch (e) {
-      // This happens if the pref doesn't exist, so we have a default value.
-      timeout = 10000;
-    }
+    let timeout = Services.prefs.getIntPref("presentation.receiver.loading.timeout", 10000);
 
     // The timer is to check if the negotiation finishes on time.
     this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     this._timer.initWithCallback(this, timeout, this._timer.TYPE_ONE_SHOT);
   },
 
   notify: function() {
     if (!this._sessionTransport) {
--- a/dom/presentation/provider/AndroidCastDeviceProvider.js
+++ b/dom/presentation/provider/AndroidCastDeviceProvider.js
@@ -353,22 +353,21 @@ ChromecastRemoteDisplayDevice.prototype 
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice,
                                          Ci.nsIPresentationLocalDevice,
                                          Ci.nsISupportsWeakReference,
                                          Ci.nsIObserver]),
 };
 
 function AndroidCastDeviceProvider() {
+  this._listener = null;
+  this._deviceList = new Map();
 }
 
 AndroidCastDeviceProvider.prototype = {
-  _listener: null,
-  _deviceList: new Map(),
-
   onSessionRequest: function APDP_onSessionRequest(aDeviceId,
                                                    aUrl,
                                                    aPresentationId,
                                                    aControlChannel) {
     log("AndroidCastDeviceProvider - onSessionRequest"
         + " aDeviceId=" + aDeviceId);
     let device = this._deviceList.get(aDeviceId);
     let receiverDevice = new ChromecastRemoteDisplayDevice(this,
@@ -398,45 +397,48 @@ AndroidCastDeviceProvider.prototype = {
 
   // nsIPresentationDeviceProvider
   set listener(aListener) {
     this._listener = aListener;
 
     // When unload this provider.
     if (!this._listener) {
       // remove observer
-      Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_ADDED);
-      Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_CHANGED);
-      Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_REMOVED);
+      EventDispatcher.instance.unregisterListener(this, [
+        TOPIC_ANDROID_CAST_DEVICE_ADDED,
+        TOPIC_ANDROID_CAST_DEVICE_CHANGED,
+        TOPIC_ANDROID_CAST_DEVICE_REMOVED,
+      ]);
       return;
     }
 
+    // Observer registration
+    EventDispatcher.instance.registerListener(this, [
+      TOPIC_ANDROID_CAST_DEVICE_ADDED,
+      TOPIC_ANDROID_CAST_DEVICE_CHANGED,
+      TOPIC_ANDROID_CAST_DEVICE_REMOVED,
+    ]);
+
     // Sync all device already found by Android.
     EventDispatcher.instance.sendRequest({ type: TOPIC_ANDROID_CAST_DEVICE_SYNCDEVICE });
-    // Observer registration
-    Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_ADDED, false);
-    Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_CHANGED, false);
-    Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_REMOVED, false);
   },
 
   get listener() {
     return this._listener;
   },
 
   forceDiscovery: function APDP_forceDiscovery() {
     // There is no API to do force discovery in Android SDK.
   },
 
-  // nsIObserver
-  observe: function APDP_observe(aSubject, aTopic, aData) {
-    log('observe ' + aTopic + ': ' + aData);
-    switch (aTopic) {
+  onEvent: function APDP_onEvent(event, data, callback) {
+    switch (event) {
       case TOPIC_ANDROID_CAST_DEVICE_ADDED:
       case TOPIC_ANDROID_CAST_DEVICE_CHANGED: {
-        let deviceInfo = JSON.parse(aData);
+        let deviceInfo = data;
         let deviceId   = deviceInfo.uuid;
 
         if (!this._deviceList.has(deviceId)) {
           let device = new ChromecastRemoteDisplayDevice(this,
                                                          deviceInfo.uuid,
                                                          deviceInfo.friendlyName,
                                                          Ci.nsIPresentationService.ROLE_CONTROLLER);
           this._deviceList.set(device.id, device);
@@ -444,17 +446,17 @@ AndroidCastDeviceProvider.prototype = {
         } else {
           let device = this._deviceList.get(deviceId);
           device.update(deviceInfo.friendlyName);
           this._listener.updateDevice(device);
         }
         break;
       }
       case TOPIC_ANDROID_CAST_DEVICE_REMOVED: {
-        let deviceId = aData;
+        let deviceId = data.id;
         if (!this._deviceList.has(deviceId)) {
           break;
         }
 
         let device   = this._deviceList.get(deviceId);
         this._listener.removeDevice(device);
         this._deviceList.delete(deviceId);
         break;
--- a/dom/system/gonk/DataCallInterfaceService.js
+++ b/dom/system/gonk/DataCallInterfaceService.js
@@ -34,22 +34,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
                                    "@mozilla.org/mobileconnection/mobileconnectionservice;1",
                                    "nsIMobileConnectionService");
 
 var DEBUG = RIL.DEBUG_RIL;
 
 function updateDebugFlag() {
   // Read debug setting from pref
-  let debugPref;
-  try {
-    debugPref = Services.prefs.getBoolPref(PREF_RIL_DEBUG_ENABLED);
-  } catch (e) {
-    debugPref = false;
-  }
+  let debugPref = Services.prefs.getBoolPref(PREF_RIL_DEBUG_ENABLED, false);
   DEBUG = debugPref || RIL.DEBUG_RIL;
 }
 updateDebugFlag();
 
 function DataCall(aAttributes) {
   for (let key in aAttributes) {
     if (key === "pdpType") {
       // Convert pdp type into constant int value.
--- a/dom/system/gonk/DataCallManager.js
+++ b/dom/system/gonk/DataCallManager.js
@@ -80,22 +80,17 @@ const NETWORK_STATE_DISCONNECTED  = Ci.n
 
 const INT32_MAX = 2147483647;
 
 // set to true in ril_consts.js to see debug messages
 var DEBUG = RIL.DEBUG_RIL;
 
 function updateDebugFlag() {
   // Read debug setting from pref
-  let debugPref;
-  try {
-    debugPref = Services.prefs.getBoolPref(PREF_RIL_DEBUG_ENABLED);
-  } catch (e) {
-    debugPref = false;
-  }
+  let debugPref = Services.prefs.getBoolPref(PREF_RIL_DEBUG_ENABLED, false);
   DEBUG = debugPref || RIL.DEBUG_RIL;
 }
 updateDebugFlag();
 
 function DataCallManager() {
   this._connectionHandlers = [];
 
   let numRadioInterfaces = gMobileConnectionService.numItems;
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -63,22 +63,17 @@ const HW_DEFAULT_CLIENT_ID = 0;
 const NETWORK_TYPE_WIFI        = Ci.nsINetworkInfo.NETWORK_TYPE_WIFI;
 const NETWORK_TYPE_MOBILE      = Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE;
 
 // set to true in ril_consts.js to see debug messages
 var DEBUG = RIL.DEBUG_RIL;
 
 function updateDebugFlag() {
   // Read debug setting from pref
-  let debugPref;
-  try {
-    debugPref = Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
-  } catch (e) {
-    debugPref = false;
-  }
+  let debugPref = Services.prefs.getBoolPref(kPrefRilDebuggingEnabled, false);
   DEBUG = RIL.DEBUG_RIL || debugPref;
 }
 updateDebugFlag();
 
 function debug(s) {
   dump("-*- RadioInterfaceLayer: " + s + "\n");
 }
 
--- a/dom/tests/mochitest/localstorage/test_localStorageQuotaPrivateBrowsing_perwindowpb.html
+++ b/dom/tests/mochitest/localstorage/test_localStorageQuotaPrivateBrowsing_perwindowpb.html
@@ -9,23 +9,17 @@
 SimpleTest.waitForExplicitFinish();
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const Ci = Components.interfaces;
 const CONTENT_PAGE = "http://mochi.test:8888/chrome/dom/tests/mochitest/localstorage/page_blank.html";
 const slavePath = "/chrome/dom/tests/mochitest/localstorage/";
 var currentTest = 1;
-var quota;
-
-try {
-  quota = Services.prefs.getIntPref("dom.storage.default_quota");
-} catch (ex) {
-  quota = 5 * 1024;
-}
+var quota = Services.prefs.getIntPref("dom.storage.default_quota", 5 * 1024);
 Services.prefs.setIntPref("browser.startup.page", 0);
 Services.prefs.setIntPref("dom.storage.default_quota", 1);
 
 var slaveLoadsPending = 1;
 var slaveOrigin = "";
 var slave = null;
 var failureRegExp = new RegExp("^FAILURE");
 
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -81,29 +81,31 @@ class JS_PUBLIC_API(JSTracer)
         // Traversing children is the responsibility of the callback.
         Callback
     };
     bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking; }
     bool isWeakMarkingTracer() const { return tag_ == TracerKindTag::WeakMarking; }
     bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; }
     bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; }
     inline JS::CallbackTracer* asCallbackTracer();
+    bool traceWeakEdges() const { return traceWeakEdges_; }
 #ifdef DEBUG
     bool checkEdges() { return checkEdges_; }
 #endif
 
   protected:
     JSTracer(JSRuntime* rt, TracerKindTag tag,
              WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
       : runtime_(rt)
       , weakMapAction_(weakTraceKind)
 #ifdef DEBUG
       , checkEdges_(true)
 #endif
       , tag_(tag)
+      , traceWeakEdges_(true)
     {}
 
 #ifdef DEBUG
     // Set whether to check edges are valid in debug builds.
     void setCheckEdges(bool check) {
         checkEdges_ = check;
     }
 #endif
@@ -112,16 +114,17 @@ class JS_PUBLIC_API(JSTracer)
     JSRuntime* runtime_;
     WeakMapTraceKind weakMapAction_;
 #ifdef DEBUG
     bool checkEdges_;
 #endif
 
   protected:
     TracerKindTag tag_;
+    bool traceWeakEdges_;
 };
 
 namespace JS {
 
 class AutoTracingName;
 class AutoTracingIndex;
 class AutoTracingCallback;
 
@@ -227,16 +230,21 @@ class JS_PUBLIC_API(CallbackTracer) : pu
     void dispatchToOnEdge(JSScript** scriptp) { onScriptEdge(scriptp); }
     void dispatchToOnEdge(js::Shape** shapep) { onShapeEdge(shapep); }
     void dispatchToOnEdge(js::ObjectGroup** groupp) { onObjectGroupEdge(groupp); }
     void dispatchToOnEdge(js::BaseShape** basep) { onBaseShapeEdge(basep); }
     void dispatchToOnEdge(js::jit::JitCode** codep) { onJitCodeEdge(codep); }
     void dispatchToOnEdge(js::LazyScript** lazyp) { onLazyScriptEdge(lazyp); }
     void dispatchToOnEdge(js::Scope** scopep) { onScopeEdge(scopep); }
 
+  protected:
+    void setTraceWeakEdges(bool value) {
+        traceWeakEdges_ = value;
+    }
+
   private:
     friend class AutoTracingName;
     const char* contextName_;
 
     friend class AutoTracingIndex;
     size_t contextIndex_;
 
     friend class AutoTracingDetails;
--- a/js/src/builtin/Reflect.cpp
+++ b/js/src/builtin/Reflect.cpp
@@ -21,17 +21,18 @@ using namespace js;
 
 /* ES6 26.1.3 Reflect.defineProperty(target, propertyKey, attributes) */
 static bool
 Reflect_defineProperty(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
-    RootedObject obj(cx, NonNullObject(cx, args.get(0)));
+    RootedObject obj(cx, NonNullObjectArg(cx, "`target`", "Reflect.defineProperty",
+                                          args.get(0)));
     if (!obj)
         return false;
 
     // Steps 2-3.
     RootedValue propertyKey(cx, args.get(1));
     RootedId key(cx);
     if (!ToPropertyKey(cx, propertyKey, &key))
         return false;
@@ -51,17 +52,18 @@ Reflect_defineProperty(JSContext* cx, un
 
 /* ES6 26.1.4 Reflect.deleteProperty (target, propertyKey) */
 static bool
 Reflect_deleteProperty(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
-    RootedObject target(cx, NonNullObject(cx, args.get(0)));
+    RootedObject target(cx, NonNullObjectArg(cx, "`target`", "Reflect.deleteProperty",
+                                             args.get(0)));
     if (!target)
         return false;
 
     // Steps 2-3.
     RootedValue propertyKey(cx, args.get(1));
     RootedId key(cx);
     if (!ToPropertyKey(cx, propertyKey, &key))
         return false;
@@ -76,17 +78,17 @@ Reflect_deleteProperty(JSContext* cx, un
 
 /* ES6 26.1.6 Reflect.get(target, propertyKey [, receiver]) */
 static bool
 Reflect_get(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
-    RootedObject obj(cx, NonNullObject(cx, args.get(0)));
+    RootedObject obj(cx, NonNullObjectArg(cx, "`target`", "Reflect.get", args.get(0)));
     if (!obj)
         return false;
 
     // Steps 2-3.
     RootedValue propertyKey(cx, args.get(1));
     RootedId key(cx);
     if (!ToPropertyKey(cx, propertyKey, &key))
         return false;
@@ -99,32 +101,33 @@ Reflect_get(JSContext* cx, unsigned argc
 }
 
 /* ES6 26.1.7 Reflect.getOwnPropertyDescriptor(target, propertyKey) */
 static bool
 Reflect_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, Value* vp)
 {
     // Step 1.
     CallArgs args = CallArgsFromVp(argc, vp);
-    if (!NonNullObject(cx, args.get(0)))
+    if (!NonNullObjectArg(cx, "`target`", "Reflect.getOwnPropertyDescriptor", args.get(0)))
         return false;
 
     // The other steps are identical to ES6 draft rev 32 (2015 Feb 2) 19.1.2.6
     // Object.getOwnPropertyDescriptor.
     return js::obj_getOwnPropertyDescriptor(cx, argc, vp);
 }
 
 /* ES6 26.1.8 Reflect.getPrototypeOf(target) */
 bool
 js::Reflect_getPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
-    RootedObject target(cx, NonNullObject(cx, args.get(0)));
+    RootedObject target(cx, NonNullObjectArg(cx, "`target`", "Reflect.getPrototypeOf",
+                                             args.get(0)));
     if (!target)
         return false;
 
     // Step 2.
     RootedObject proto(cx);
     if (!GetPrototype(cx, target, &proto))
         return false;
     args.rval().setObjectOrNull(proto);
@@ -133,17 +136,17 @@ js::Reflect_getPrototypeOf(JSContext* cx
 
 /* ES6 draft 26.1.10 Reflect.isExtensible(target) */
 bool
 js::Reflect_isExtensible(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
-    RootedObject target(cx, NonNullObject(cx, args.get(0)));
+    RootedObject target(cx, NonNullObjectArg(cx, "`target`", "Reflect.isExtensible", args.get(0)));
     if (!target)
         return false;
 
     // Step 2.
     bool extensible;
     if (!IsExtensible(cx, target, &extensible))
         return false;
     args.rval().setBoolean(extensible);
@@ -152,31 +155,32 @@ js::Reflect_isExtensible(JSContext* cx, 
 
 /* ES6 26.1.11 Reflect.ownKeys(target) */
 static bool
 Reflect_ownKeys(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
-    if (!NonNullObject(cx, args.get(0)))
+    if (!NonNullObjectArg(cx, "`target`", "Reflect.ownKeys", args.get(0)))
         return false;
 
     // Steps 2-4.
     return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
 }
 
 /* ES6 26.1.12 Reflect.preventExtensions(target) */
 static bool
 Reflect_preventExtensions(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
-    RootedObject target(cx, NonNullObject(cx, args.get(0)));
+    RootedObject target(cx, NonNullObjectArg(cx, "`target`", "Reflect.preventExtensions",
+                                             args.get(0)));
     if (!target)
         return false;
 
     // Step 2.
     ObjectOpResult result;
     if (!PreventExtensions(cx, target, result))
         return false;
     args.rval().setBoolean(bool(result));
@@ -185,17 +189,17 @@ Reflect_preventExtensions(JSContext* cx,
 
 /* ES6 26.1.13 Reflect.set(target, propertyKey, V [, receiver]) */
 static bool
 Reflect_set(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
-    RootedObject target(cx, NonNullObject(cx, args.get(0)));
+    RootedObject target(cx, NonNullObjectArg(cx, "`target`", "Reflect.set", args.get(0)));
     if (!target)
         return false;
 
     // Steps 2-3.
     RootedValue propertyKey(cx, args.get(1));
     RootedId key(cx);
     if (!ToPropertyKey(cx, propertyKey, &key))
         return false;
@@ -219,17 +223,17 @@ Reflect_set(JSContext* cx, unsigned argc
  * share code.
  */
 static bool
 Reflect_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
-    RootedObject obj(cx, NonNullObject(cx, args.get(0)));
+    RootedObject obj(cx, NonNullObjectArg(cx, "`target`", "Reflect.setPrototypeOf", args.get(0)));
     if (!obj)
         return false;
 
     // Step 2.
     if (!args.get(1).isObjectOrNull()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
                                   "Reflect.setPrototypeOf", "an object or null",
                                   InformalValueTypeName(args.get(1)));
--- a/js/src/builtin/Reflect.js
+++ b/js/src/builtin/Reflect.js
@@ -29,18 +29,20 @@ function CreateListFromArrayLikeForArgs(
 // ES2017 draft rev a785b0832b071f505a694e1946182adeab84c972
 // 26.1.1 Reflect.apply ( target, thisArgument, argumentsList )
 function Reflect_apply(target, thisArgument, argumentsList) {
     // Step 1.
     if (!IsCallable(target))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, target));
 
     // Step 2.
-    if (!IsObject(argumentsList))
-        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(2, argumentsList));
+    if (!IsObject(argumentsList)) {
+        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT_ARG, "`argumentsList`", "Reflect.apply",
+                       ToSource(argumentsList));
+    }
 
     // Steps 2-4.
     return callFunction(std_Function_apply, target, thisArgument, argumentsList);
 }
 
 // ES2017 draft rev a785b0832b071f505a694e1946182adeab84c972
 // 26.1.2 Reflect.construct ( target, argumentsList [ , newTarget ] )
 function Reflect_construct(target, argumentsList/*, newTarget*/) {
@@ -54,18 +56,20 @@ function Reflect_construct(target, argum
         newTarget = arguments[2];
         if (!IsConstructor(newTarget))
             ThrowTypeError(JSMSG_NOT_CONSTRUCTOR, DecompileArg(2, newTarget));
     } else {
         newTarget = target;
     }
 
     // Step 4.
-    if (!IsObject(argumentsList))
-        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(1, argumentsList));
+    if (!IsObject(argumentsList)) {
+        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT_ARG, "`argumentsList`", "Reflect.construct",
+                       ToSource(argumentsList));
+    }
 
     // Fast path when we can avoid calling CreateListFromArrayLikeForArgs().
     var args = (IsPackedArray(argumentsList) && argumentsList.length <= MAX_ARGS_LENGTH)
                ? argumentsList
                : CreateListFromArrayLikeForArgs(argumentsList);
 
     // Step 5.
     switch (args.length) {
@@ -100,13 +104,14 @@ function Reflect_construct(target, argum
     }
 }
 
 // ES2017 draft rev a785b0832b071f505a694e1946182adeab84c972
 // 26.1.8 Reflect.has ( target, propertyKey )
 function Reflect_has(target, propertyKey) {
     // Step 1.
     if (!IsObject(target))
-        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(0, target));
+        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT_ARG, "`target`", "Reflect.has",
+                       ToSource(target));
 
     // Steps 2-3 are identical to the runtime semantics of the "in" operator.
     return propertyKey in target;
 }
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -164,21 +164,17 @@ SetWeakMapEntryInternal(JSContext* cx, H
 }
 
 MOZ_ALWAYS_INLINE bool
 WeakMap_set_impl(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(IsWeakMap(args.thisv()));
 
     if (!args.get(0).isObject()) {
-        UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr);
-        if (!bytes)
-            return false;
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
-                                   bytes.get());
+        ReportNotObjectWithName(cx, "WeakMap key", args.get(0));
         return false;
     }
 
     RootedObject key(cx, &args[0].toObject());
     Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
     Rooted<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
 
     if (!SetWeakMapEntryInternal(cx, map, key, args.get(1)))
--- a/js/src/builtin/WeakSet.js
+++ b/js/src/builtin/WeakSet.js
@@ -28,17 +28,17 @@ function WeakSet_add(value) {
 
     // Step 4.,6.
     let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
     if (!entries)
         ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S);
 
     // Step 5.
     if (!IsObject(value))
-        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(0, value));
+        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT_NAME, "WeakSet value", ToSource(value));
 
     // Steps 7-8.
     callFunction(std_WeakMap_set, entries, value, true);
 
     // Step 8.
     return S;
 }
 
--- a/js/src/builtin/WeakSetObject.cpp
+++ b/js/src/builtin/WeakSetObject.cpp
@@ -113,22 +113,17 @@ WeakSetObject::construct(JSContext* cx, 
             RootedValue placeholder(cx, BooleanValue(true));
             RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject());
             RootedArrayObject array(cx, &iterable.toObject().as<ArrayObject>());
             for (uint32_t index = 0; index < array->getDenseInitializedLength(); ++index) {
                 keyVal.set(array->getDenseElement(index));
                 MOZ_ASSERT(!keyVal.isMagic(JS_ELEMENTS_HOLE));
 
                 if (keyVal.isPrimitive()) {
-                    UniqueChars bytes =
-                        DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr);
-                    if (!bytes)
-                        return false;
-                    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                                               JSMSG_NOT_NONNULL_OBJECT, bytes.get());
+                    ReportNotObjectWithName(cx, "WeakSet value", keyVal);
                     return false;
                 }
 
                 keyObject = &keyVal.toObject();
                 if (!SetWeakMapEntry(cx, map, keyObject, placeholder))
                     return false;
             }
         } else {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -59,39 +59,42 @@ namespace frontend {
 
 using DeclaredNamePtr = ParseContext::Scope::DeclaredNamePtr;
 using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr;
 using BindingIter = ParseContext::Scope::BindingIter;
 using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
 
 // Read a token. Report an error and return null() if that token doesn't match
 // to the condition.  Do not use MUST_MATCH_TOKEN_INTERNAL directly.
-#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorNumber)                              \
+#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorReport)                              \
     JS_BEGIN_MACRO                                                                          \
         TokenKind token;                                                                    \
         if (!tokenStream.getToken(&token, modifier))                                        \
             return null();                                                                  \
         if (!(cond)) {                                                                      \
-            error(errorNumber);                                                             \
+            errorReport;                                                                    \
             return null();                                                                  \
         }                                                                                   \
     JS_END_MACRO
 
 #define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \
-    MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorNumber)
+    MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, error(errorNumber))
 
 #define MUST_MATCH_TOKEN(tt, errorNumber) \
     MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errorNumber)
 
 #define MUST_MATCH_TOKEN_FUNC_MOD(func, modifier, errorNumber) \
-    MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, errorNumber)
+    MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, error(errorNumber))
 
 #define MUST_MATCH_TOKEN_FUNC(func, errorNumber) \
     MUST_MATCH_TOKEN_FUNC_MOD(func, TokenStream::None, errorNumber)
 
+#define MUST_MATCH_TOKEN_MOD_WITH_REPORT(tt, modifier, errorReport) \
+    MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorReport)
+
 template <class T, class U>
 static inline void
 PropagateTransitiveParseFlags(const T* inner, U* outer)
 {
     if (inner->bindingsAccessedDynamically())
         outer->setBindingsAccessedDynamically();
     if (inner->hasDebuggerStatement())
         outer->setHasDebuggerStatement();
@@ -998,16 +1001,45 @@ Parser<ParseHandler>::hasValidSimpleStri
         if (!isValidStrictBinding(name->asPropertyName()))
             return false;
     }
     return true;
 }
 
 template <typename ParseHandler>
 void
+Parser<ParseHandler>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber,
+                                           uint32_t openedPos)
+{
+    auto notes = MakeUnique<JSErrorNotes>();
+    if (!notes)
+        return;
+
+    uint32_t line, column;
+    tokenStream.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column);
+
+    const size_t MaxWidth = sizeof("4294967295");
+    char columnNumber[MaxWidth];
+    SprintfLiteral(columnNumber, "%" PRIu32, column);
+    char lineNumber[MaxWidth];
+    SprintfLiteral(lineNumber, "%" PRIu32, line);
+
+    if (!notes->addNoteASCII(pc->sc()->context,
+                             getFilename(), line, column,
+                             GetErrorMessage, nullptr,
+                             noteNumber, lineNumber, columnNumber))
+    {
+        return;
+    }
+
+    errorWithNotes(Move(notes), errorNumber);
+}
+
+template <typename ParseHandler>
+void
 Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind,
                                           TokenPos pos, uint32_t prevPos)
 {
     JSAutoByteString bytes;
     if (!AtomToPrintableString(context, name, &bytes))
         return;
 
     if (prevPos == DeclaredNameInfo::npos) {
@@ -1023,21 +1055,21 @@ Parser<ParseHandler>::reportRedeclaratio
     tokenStream.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
 
     const size_t MaxWidth = sizeof("4294967295");
     char columnNumber[MaxWidth];
     SprintfLiteral(columnNumber, "%" PRIu32, column);
     char lineNumber[MaxWidth];
     SprintfLiteral(lineNumber, "%" PRIu32, line);
 
-    if (!notes->addNoteLatin1(pc->sc()->context,
-                              getFilename(), line, column,
-                              GetErrorMessage, nullptr,
-                              JSMSG_REDECLARED_PREV,
-                              lineNumber, columnNumber))
+    if (!notes->addNoteASCII(pc->sc()->context,
+                             getFilename(), line, column,
+                             GetErrorMessage, nullptr,
+                             JSMSG_REDECLARED_PREV,
+                             lineNumber, columnNumber))
     {
         return;
     }
 
     errorWithNotesAt(Move(notes), pos.begin, JSMSG_REDECLARED_VAR,
                      DeclarationKindString(prevKind), bytes.ptr());
 }
 
@@ -3602,16 +3634,17 @@ Parser<ParseHandler>::functionFormalPara
         return false;
     }
 
     // Parse the function body.
     FunctionBodyType bodyType = StatementListBody;
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
+    uint32_t openedPos = 0;
     if (tt != TOK_LC) {
         if (funbox->isStarGenerator() || kind == Method ||
             kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
             IsConstructorKind(kind)) {
             error(JSMSG_CURLY_BEFORE_BODY);
             return false;
         }
 
@@ -3624,16 +3657,18 @@ Parser<ParseHandler>::functionFormalPara
             error(JSMSG_CURLY_BEFORE_BODY);
             return false;
 #endif
         }
 
         tokenStream.ungetToken();
         bodyType = ExpressionBody;
         funbox->setIsExprBody();
+    } else {
+        openedPos = pos().begin;
     }
 
     // Arrow function parameters inherit yieldHandling from the enclosing
     // context, but the arrow body doesn't. E.g. in |(a = yield) => yield|,
     // |yield| in the parameters is either a name or keyword, depending on
     // whether the arrow function is enclosed in a generator function or not.
     // Whereas the |yield| in the function body is always parsed as a name.
     YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind());
@@ -3661,23 +3696,19 @@ Parser<ParseHandler>::functionFormalPara
                                         nameYieldHandling))
             {
                 return false;
             }
         }
     }
 
     if (bodyType == StatementListBody) {
-        bool matched;
-        if (!tokenStream.matchToken(&matched, TOK_RC, TokenStream::Operand))
-            return false;
-        if (!matched) {
-            error(JSMSG_CURLY_AFTER_BODY);
-            return false;
-        }
+        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+                                         reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
+                                                              JSMSG_CURLY_OPENED, openedPos));
         funbox->bufEnd = pos().end;
     } else {
 #if !JS_HAS_EXPR_CLOSURES
         MOZ_ASSERT(kind == Arrow);
 #endif
         if (tokenStream.hadError())
             return false;
         funbox->bufEnd = pos().end;
@@ -4441,27 +4472,30 @@ Parser<ParseHandler>::destructuringDecla
     return res;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+    uint32_t openedPos = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::Block);
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
         return null();
 
     Node list = statementList(yieldHandling);
     if (!list)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, errorNumber);
+    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+                                     reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
+                                                          openedPos));
 
     return finishLexicalScope(scope, list);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expressionAfterForInOrOf(ParseNodeKind forHeadKind,
                                                YieldHandling yieldHandling)
@@ -6700,30 +6734,34 @@ Parser<ParseHandler>::tryStatement(Yield
      *
      * finally nodes are TOK_LC statement lists.
      */
 
     Node innerBlock;
     {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
 
+        uint32_t openedPos = pos().begin;
+
         ParseContext::Statement stmt(pc, StatementKind::Try);
         ParseContext::Scope scope(this);
         if (!scope.init(pc))
             return null();
 
         innerBlock = statementList(yieldHandling);
         if (!innerBlock)
             return null();
 
         innerBlock = finishLexicalScope(scope, innerBlock);
         if (!innerBlock)
             return null();
 
-        MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_TRY);
+        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+                                         reportMissingClosing(JSMSG_CURLY_AFTER_TRY,
+                                                              JSMSG_CURLY_OPENED, openedPos));
     }
 
     bool hasUnconditionalCatch = false;
     Node catchList = null();
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
     if (tt == TOK_CATCH) {
@@ -6829,46 +6867,52 @@ Parser<ParseHandler>::tryStatement(Yield
         } while (tt == TOK_CATCH);
     }
 
     Node finallyBlock = null();
 
     if (tt == TOK_FINALLY) {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
 
+        uint32_t openedPos = pos().begin;
+
         ParseContext::Statement stmt(pc, StatementKind::Finally);
         ParseContext::Scope scope(this);
         if (!scope.init(pc))
             return null();
 
         finallyBlock = statementList(yieldHandling);
         if (!finallyBlock)
             return null();
 
         finallyBlock = finishLexicalScope(scope, finallyBlock);
         if (!finallyBlock)
             return null();
 
-        MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_FINALLY);
+        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+                                         reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY,
+                                                              JSMSG_CURLY_OPENED, openedPos));
     } else {
         tokenStream.ungetToken();
     }
     if (!catchList && !finallyBlock) {
         error(JSMSG_CATCH_OR_FINALLY);
         return null();
     }
 
     return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling,
                                           ParseContext::Scope& catchParamScope)
 {
+    uint32_t openedPos = pos().begin;
+
     ParseContext::Statement stmt(pc, StatementKind::Block);
 
     // ES 13.15.7 CatchClauseEvaluation
     //
     // Step 8 means that the body of a catch block always has an additional
     // lexical scope.
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
@@ -6878,17 +6922,19 @@ Parser<ParseHandler>::catchBlockStatemen
     // block, so declare the name in the inner scope.
     if (!scope.addCatchParameters(pc, catchParamScope))
         return null();
 
     Node list = statementList(yieldHandling);
     if (!list)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_CATCH);
+    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+                                     reportMissingClosing(JSMSG_CURLY_AFTER_CATCH,
+                                                          JSMSG_CURLY_OPENED, openedPos));
 
     // The catch parameter names are not bound in the body scope, so remove
     // them before generating bindings.
     scope.removeCatchParameters(pc, catchParamScope);
     return finishLexicalScope(scope, list);
 }
 
 template <typename ParseHandler>
@@ -9214,17 +9260,19 @@ Parser<ParseHandler>::arrayInitializer(Y
                     modifier = TokenStream::None;
                     break;
                 }
                 if (tt == TOK_TRIPLEDOT && possibleError)
                     possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
             }
         }
 
-        MUST_MATCH_TOKEN_MOD(TOK_RB, modifier, JSMSG_BRACKET_AFTER_LIST);
+        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
+                                         reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
+                                                              JSMSG_BRACKET_OPENED, begin));
     }
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 static JSAtom*
 DoubleToAtom(JSContext* cx, double value)
 {
@@ -9435,16 +9483,18 @@ Parser<ParseHandler>::computedPropertyNa
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
 
+    uint32_t openedPos = pos().begin;
+
     Node literal = handler.newObjectLiteral(pos().begin);
     if (!literal)
         return null();
 
     bool seenPrototypeMutation = false;
     bool seenCoverInitializedName = false;
     RootedAtom propAtom(context);
     for (;;) {
@@ -9597,17 +9647,17 @@ Parser<ParseHandler>::objectLiteral(Yiel
                 return null();
         }
 
         if (!tokenStream.getToken(&tt))
             return null();
         if (tt == TOK_RC)
             break;
         if (tt != TOK_COMMA) {
-            error(JSMSG_CURLY_AFTER_LIST);
+            reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos);
             return null();
         }
     }
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1443,16 +1443,18 @@ class Parser final : public ParserBase, 
                                        FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
 
   private:
     bool checkIncDecOperand(Node operand, uint32_t operandOffset);
     bool checkStrictAssignment(Node lhs);
 
     bool hasValidSimpleStrictParameterNames();
 
+    void reportMissingClosing(unsigned errorNumber, unsigned noteNumber, uint32_t openedPos);
+
     void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos,
                              uint32_t prevPos);
     bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos,
                                        bool disallowDuplicateParams, bool* duplicatedParam);
     bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
     mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
                                                           DeclarationKind kind);
     bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -974,17 +974,17 @@ class GCRuntime
     template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::Phase phase);
     void markBufferedGrayRoots(JS::Zone* zone);
     void markGrayReferencesInCurrentGroup(gcstats::Phase phase);
     void markAllWeakReferences(gcstats::Phase phase);
     void markAllGrayReferences(gcstats::Phase phase);
 
     void beginSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
     void findZoneGroups(AutoLockForExclusiveAccess& lock);
-    MOZ_MUST_USE bool findZoneEdgesForWeakMaps();
+    MOZ_MUST_USE bool findInterZoneEdges();
     void getNextZoneGroup();
     void endMarkingZoneGroup();
     void beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock);
     bool shouldReleaseObservedTypes();
     void endSweepingZoneGroup();
     IncrementalProgress sweepPhase(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock);
     void endSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
     bool allCCVisibleZonesWereCollected() const;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -474,19 +474,22 @@ js::UnsafeTraceManuallyBarrieredEdge(JST
 {
     DispatchToTracer(trc, ConvertToBase(thingp), name);
 }
 
 template <typename T>
 void
 js::TraceWeakEdge(JSTracer* trc, WeakRef<T>* thingp, const char* name)
 {
-    // Non-marking tracers treat the edge strongly.
-    if (!trc->isMarkingTracer())
-        return DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
+    if (!trc->isMarkingTracer()) {
+        // Non-marking tracers can select whether or not they see weak edges.
+        if (trc->traceWeakEdges())
+            DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
+        return;
+    }
 
     NoteWeakEdge(GCMarker::fromTracer(trc),
                  ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
 }
 
 template <typename T>
 void
 js::TraceRoot(JSTracer* trc, T* thingp, const char* name)
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -444,24 +444,34 @@ js::gc::GCRuntime::finishVerifier()
     if (verifyPreData) {
         js_delete(verifyPreData.ref());
         verifyPreData = nullptr;
     }
 }
 
 #endif /* JS_GC_ZEAL */
 
-#ifdef JSGC_HASH_TABLE_CHECKS
+#if defined(JSGC_HASH_TABLE_CHECKS) || defined(DEBUG)
 
-class CheckHeapTracer : public JS::CallbackTracer
+class HeapCheckTracerBase : public JS::CallbackTracer
 {
   public:
-    explicit CheckHeapTracer(JSRuntime* rt);
+    explicit HeapCheckTracerBase(JSRuntime* rt, WeakMapTraceKind weakTraceKind);
     bool init();
-    void check(AutoLockForExclusiveAccess& lock);
+    bool traceHeap(AutoLockForExclusiveAccess& lock);
+    virtual void checkCell(Cell* cell) = 0;
+
+  protected:
+    void dumpCellPath();
+
+    Cell* parentCell() {
+        return parentIndex == -1 ? nullptr : stack[parentIndex].thing.asCell();
+    }
+
+    size_t failures;
 
   private:
     void onChild(const JS::GCCellPtr& thing) override;
 
     struct WorkItem {
         WorkItem(JS::GCCellPtr thing, const char* name, int parentIndex)
           : thing(thing), name(name), parentIndex(parentIndex), processed(false)
         {}
@@ -469,114 +479,216 @@ class CheckHeapTracer : public JS::Callb
         JS::GCCellPtr thing;
         const char* name;
         int parentIndex;
         bool processed;
     };
 
     JSRuntime* rt;
     bool oom;
-    size_t failures;
     HashSet<Cell*, DefaultHasher<Cell*>, SystemAllocPolicy> visited;
     Vector<WorkItem, 0, SystemAllocPolicy> stack;
     int parentIndex;
 };
 
-CheckHeapTracer::CheckHeapTracer(JSRuntime* rt)
-  : CallbackTracer(rt, TraceWeakMapKeysValues),
+HeapCheckTracerBase::HeapCheckTracerBase(JSRuntime* rt, WeakMapTraceKind weakTraceKind)
+  : CallbackTracer(rt, weakTraceKind),
+    failures(0),
     rt(rt),
     oom(false),
-    failures(0),
     parentIndex(-1)
 {
 #ifdef DEBUG
     setCheckEdges(false);
 #endif
 }
 
 bool
-CheckHeapTracer::init()
+HeapCheckTracerBase::init()
 {
     return visited.init();
 }
 
-inline static bool
-IsValidGCThingPointer(Cell* cell)
-{
-    return (uintptr_t(cell) & CellMask) == 0;
-}
-
 void
-CheckHeapTracer::onChild(const JS::GCCellPtr& thing)
+HeapCheckTracerBase::onChild(const JS::GCCellPtr& thing)
 {
     Cell* cell = thing.asCell();
+    checkCell(cell);
+
     if (visited.lookup(cell))
         return;
 
     if (!visited.put(cell)) {
         oom = true;
         return;
     }
 
-    if (!IsValidGCThingPointer(cell) || !IsGCThingValidAfterMovingGC(cell))
-    {
-        failures++;
-        fprintf(stderr, "Bad pointer %p\n", cell);
-        const char* name = contextName();
-        for (int index = parentIndex; index != -1; index = stack[index].parentIndex) {
-            const WorkItem& parent = stack[index];
-            cell = parent.thing.asCell();
-            fprintf(stderr, "  from %s %p %s edge\n",
-                    GCTraceKindToAscii(cell->getTraceKind()), cell, name);
-            name = parent.name;
-        }
-        fprintf(stderr, "  from root %s\n", name);
-        return;
-    }
-
     // Don't trace into GC things owned by another runtime.
     if (cell->runtimeFromAnyThread() != rt)
         return;
 
+    // Don't trace into GC in zones being used by helper threads.
+    Zone* zone = thing.is<JSObject>() ? thing.as<JSObject>().zone() : cell->asTenured().zone();
+    if (zone->group() && zone->group()->usedByHelperThread)
+        return;
+
     WorkItem item(thing, contextName(), parentIndex);
     if (!stack.append(item))
         oom = true;
 }
 
-void
-CheckHeapTracer::check(AutoLockForExclusiveAccess& lock)
+bool
+HeapCheckTracerBase::traceHeap(AutoLockForExclusiveAccess& lock)
 {
     // The analysis thinks that traceRuntime might GC by calling a GC callback.
     JS::AutoSuppressGCAnalysis nogc;
     if (!rt->isBeingDestroyed())
         rt->gc.traceRuntime(this, lock);
 
-    while (!stack.empty()) {
+    while (!stack.empty() && !oom) {
         WorkItem item = stack.back();
         if (item.processed) {
             stack.popBack();
         } else {
             parentIndex = stack.length() - 1;
+            stack.back().processed = true;
             TraceChildren(this, item.thing);
-            stack.back().processed = true;
         }
     }
 
-    if (oom)
+    return !oom;
+}
+
+void
+HeapCheckTracerBase::dumpCellPath()
+{
+    const char* name = contextName();
+    for (int index = parentIndex; index != -1; index = stack[index].parentIndex) {
+        const WorkItem& parent = stack[index];
+        Cell* cell = parent.thing.asCell();
+        fprintf(stderr, "  from %s %p %s edge\n",
+                GCTraceKindToAscii(cell->getTraceKind()), cell, name);
+        name = parent.name;
+    }
+    fprintf(stderr, "  from root %s\n", name);
+}
+
+#endif // defined(JSGC_HASH_TABLE_CHECKS) || defined(DEBUG)
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+
+class CheckHeapTracer final : public HeapCheckTracerBase
+{
+  public:
+    explicit CheckHeapTracer(JSRuntime* rt);
+    void check(AutoLockForExclusiveAccess& lock);
+
+  private:
+    void checkCell(Cell* cell) override;
+};
+
+CheckHeapTracer::CheckHeapTracer(JSRuntime* rt)
+  : HeapCheckTracerBase(rt, TraceWeakMapKeysValues)
+{}
+
+inline static bool
+IsValidGCThingPointer(Cell* cell)
+{
+    return (uintptr_t(cell) & CellMask) == 0;
+}
+
+void
+CheckHeapTracer::checkCell(Cell* cell)
+{
+    if (!IsValidGCThingPointer(cell) || !IsGCThingValidAfterMovingGC(cell)) {
+        failures++;
+        fprintf(stderr, "Bad pointer %p\n", cell);
+        dumpCellPath();
+    }
+}
+
+void
+CheckHeapTracer::check(AutoLockForExclusiveAccess& lock)
+{
+    if (!traceHeap(lock))
         return;
 
-    if (failures) {
-        fprintf(stderr, "Heap check: %" PRIuSIZE " failure(s) out of %" PRIu32 " pointers checked\n",
-                failures, visited.count());
-    }
+    if (failures)
+        fprintf(stderr, "Heap check: %" PRIuSIZE " failure(s)\n", failures);
     MOZ_RELEASE_ASSERT(failures == 0);
 }
 
 void
 js::gc::CheckHeapAfterGC(JSRuntime* rt)
 {
     AutoTraceSession session(rt, JS::HeapState::Tracing);
     CheckHeapTracer tracer(rt);
     if (tracer.init())
         tracer.check(session.lock);
 }
 
 #endif /* JSGC_HASH_TABLE_CHECKS */
+
+#ifdef DEBUG
+
+class CheckGrayMarkingTracer final : public HeapCheckTracerBase
+{
+  public:
+    explicit CheckGrayMarkingTracer(JSRuntime* rt);
+    bool check(AutoLockForExclusiveAccess& lock);
+
+  private:
+    void checkCell(Cell* cell) override;
+};
+
+CheckGrayMarkingTracer::CheckGrayMarkingTracer(JSRuntime* rt)
+  : HeapCheckTracerBase(rt, DoNotTraceWeakMaps)
+{
+    // Weak gray->black edges are allowed.
+    setTraceWeakEdges(false);
+}
+
+void
+CheckGrayMarkingTracer::checkCell(Cell* cell)
+{
+    Cell* parent = parentCell();
+    if (!cell->isTenured() || !parent || !parent->isTenured())
+        return;
+
+    TenuredCell* tenuredCell = &cell->asTenured();
+    TenuredCell* tenuredParent = &parent->asTenured();
+    if (tenuredParent->isMarked(BLACK) && !tenuredParent->isMarked(GRAY) &&
+        tenuredCell->isMarked(GRAY))
+    {
+        failures++;
+        fprintf(stderr, "Found black to gray edge %p\n", cell);
+        dumpCellPath();
+    }
+}
+
+bool
+CheckGrayMarkingTracer::check(AutoLockForExclusiveAccess& lock)
+{
+    if (!traceHeap(lock))
+        return true; // Ignore failure.
+
+    return failures == 0;
+}
+
+JS_FRIEND_API(bool)
+js::CheckGrayMarkingState(JSContext* cx)
+{
+    JSRuntime* rt = cx->runtime();
+    MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
+    MOZ_ASSERT(!rt->gc.isIncrementalGCInProgress());
+    if (!rt->gc.areGrayBitsValid())
+        return true;
+
+    gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PHASE_TRACE_HEAP);
+    AutoTraceSession session(rt, JS::HeapState::Tracing);
+    CheckGrayMarkingTracer tracer(rt);
+    if (!tracer.init())
+        return true; // Ignore failure
+
+    return tracer.check(session.lock);
+}
+
+#endif // DEBUG
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -33,16 +33,17 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup*
     types(this),
     gcWeakMapList_(group),
     compartments_(),
     gcGrayRoots_(group),
     gcWeakRefs_(group),
     weakCaches_(group),
     gcWeakKeys_(group, SystemAllocPolicy(), rt->randomHashCodeScrambler()),
     gcZoneGroupEdges_(group),
+    hasDeadProxies_(group),
     typeDescrObjects_(group, this, SystemAllocPolicy()),
     markedAtoms_(group),
     usage(&rt->gc.usage),
     threshold(),
     gcDelayBytes(0),
     propertyTree_(group, this),
     baseShapes_(group, this, BaseShapeSet()),
     initialShapes_(group, this, InitialShapeSet()),
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -366,19 +366,27 @@ struct Zone : public JS::shadow::Zone,
     js::gc::WeakKeyTable& gcWeakKeys() { return gcWeakKeys_.ref(); }
 
   private:
     // A set of edges from this zone to other zones.
     //
     // This is used during GC while calculating zone groups to record edges that
     // can't be determined by examining this zone by itself.
     js::ZoneGroupData<ZoneSet> gcZoneGroupEdges_;
+
+    // Zones with dead proxies require an extra scan through the wrapper map,
+    // so track whether any dead proxies are known to exist.
+    js::ZoneGroupData<bool> hasDeadProxies_;
+
   public:
     ZoneSet& gcZoneGroupEdges() { return gcZoneGroupEdges_.ref(); }
 
+    bool hasDeadProxies() { return hasDeadProxies_; }
+    void setHasDeadProxies(bool b) { hasDeadProxies_ = b; }
+
     // Keep track of all TypeDescr and related objects in this compartment.
     // This is used by the GC to trace them all first when compacting, since the
     // TypedObject trace hook may access these objects.
     //
     // There are no barriers here - the set contains only tenured objects so no
     // post-barrier is required, and these are weak references so no pre-barrier
     // is required.
     using TypeDescrObjectSet = js::GCHashSet<JSObject*,
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/bug1344334.js
@@ -0,0 +1,14 @@
+if (!('oomTest' in this))
+    quit();
+
+function f(s) {
+    s + "x";
+    s.indexOf("y") === 0;
+    oomTest(new Function(s));
+}
+var s = `
+    class TestClass { constructor() {} }
+    for (var fun of hasPrototype) {}
+`;
+if (s.length)
+    f(s);
--- a/js/src/jit-test/tests/cacheir/nukedCCW.js
+++ b/js/src/jit-test/tests/cacheir/nukedCCW.js
@@ -1,11 +1,11 @@
-var wrapper = evaluate("({a: 15, b: {c: 42}})", {global: newGlobal({sameZoneAs: this})});
+function testNuke() {
+    var wrapper = evaluate("({a: 15, b: {c: 42}})", {global: newGlobal({sameZoneAs: this})});
 
-function test() {
     var i, error;
     try {
         for (i = 0; i < 150; i++) {
             assertEq(wrapper.b.c, 42);
             assertEq(wrapper.a, 15);
 
             if (i == 142) {
                 // Next access to wrapper.b should throw.
@@ -15,9 +15,24 @@ function test() {
     } catch (e) {
         error = e;
     }
 
     assertEq(error.message.includes("dead object"), true);
     assertEq(i, 143);
 }
 
-test();
+function testSweep() {
+    var wrapper = evaluate("({a: 15, b: {c: 42}})", {global: newGlobal({})});
+    var error;
+    nukeCCW(wrapper);
+    gczeal(8, 1); // Sweep zones separately
+    try {
+      // Next access to wrapper.b should throw.
+      wrapper.x = 4;
+    } catch (e) {
+        error = e;
+    }
+    assertEq(error.message.includes("dead object"), true);
+}
+
+testNuke();
+testSweep();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/missing-closing-brace.js
@@ -0,0 +1,90 @@
+function test(source, [lineNumber, columnNumber], openType = "{", closeType = "}") {
+  let caught = false;
+  try {
+    Reflect.parse(source, { source: "foo.js" });
+  } catch (e) {
+    assertEq(e.message.includes("missing " + closeType + " "), true);
+    let notes = getErrorNotes(e);
+    assertEq(notes.length, 1);
+    let note = notes[0];
+    assertEq(note.message, openType + " opened at line " + lineNumber + ", column " + columnNumber);
+    assertEq(note.fileName, "foo.js");
+    assertEq(note.lineNumber, lineNumber);
+    assertEq(note.columnNumber, columnNumber);
+    caught = true;
+  }
+  assertEq(caught, true);
+}
+
+// Function
+
+test(`
+function test1() {
+}
+function test2() {
+  if (true) {
+  //}
+}
+function test3() {
+}
+`, [4, 17]);
+
+// Block statement.
+test(`
+{
+  if (true) {
+}
+`, [2, 0]);
+test(`
+if (true) {
+  if (true) {
+}
+`, [2, 10]);
+test(`
+for (;;) {
+  if (true) {
+}
+`, [2, 9]);
+test(`
+while (true) {
+  if (true) {
+}
+`, [2, 13]);
+test(`
+do {
+  do {
+} while(true);
+`, [2, 3]);
+
+// try-catch-finally.
+test(`
+try {
+  if (true) {
+}
+`, [2, 4]);
+test(`
+try {
+} catch (e) {
+  if (true) {
+}
+`, [3, 12]);
+test(`
+try {
+} finally {
+  if (true) {
+}
+`, [3, 10]);
+
+// Object literal.
+test(`
+var x = {
+  foo: {
+};
+`, [2, 8]);
+
+// Array literal.
+test(`
+var x = [
+  [
+];
+`, [2, 8], "[", "]");
--- a/js/src/jit/AliasAnalysisShared.cpp
+++ b/js/src/jit/AliasAnalysisShared.cpp
@@ -136,16 +136,17 @@ GetObject(const MDefinition* ins)
       case MDefinition::Op_SetArgumentsObjectArg:
       case MDefinition::Op_GetFrameArgument:
       case MDefinition::Op_SetFrameArgument:
       case MDefinition::Op_CompareExchangeTypedArrayElement:
       case MDefinition::Op_AtomicExchangeTypedArrayElement:
       case MDefinition::Op_AtomicTypedArrayElementBinop:
       case MDefinition::Op_AsmJSLoadHeap:
       case MDefinition::Op_AsmJSStoreHeap:
+      case MDefinition::Op_WasmLoadTls:
       case MDefinition::Op_WasmLoad:
       case MDefinition::Op_WasmStore:
       case MDefinition::Op_AsmJSCompareExchangeHeap:
       case MDefinition::Op_AsmJSAtomicBinopHeap:
       case MDefinition::Op_WasmLoadGlobalVar:
       case MDefinition::Op_WasmStoreGlobalVar:
       case MDefinition::Op_ArrayJoin:
         return nullptr;
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -324,17 +324,24 @@ DoTypeUpdateFallback(JSContext* cx, Base
         JSObject* maybeSingleton = obj->isSingleton() ? obj.get() : nullptr;
         AddTypePropertyId(cx, group, maybeSingleton, id, value);
         break;
       }
       default:
         MOZ_CRASH("Invalid stub");
     }
 
-    return stub->addUpdateStubForValue(cx, script /* = outerScript */, obj, id, value);
+    if (!stub->addUpdateStubForValue(cx, script /* = outerScript */, obj, id, value)) {
+        // The calling JIT code assumes this function is infallible (for
+        // instance we may reallocate dynamic slots before calling this),
+        // so ignore OOMs if we failed to attach a stub.
+        cx->recoverFromOutOfMemory();
+    }
+
+    return true;
 }
 
 typedef bool (*DoTypeUpdateFallbackFn)(JSContext*, BaselineFrame*, ICUpdatedStub*, HandleValue,
                                        HandleValue);
 const VMFunction DoTypeUpdateFallbackInfo =
     FunctionInfo<DoTypeUpdateFallbackFn>(DoTypeUpdateFallback, "DoTypeUpdateFallback", NonTailCall);
 
 bool
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -12000,19 +12000,42 @@ CodeGenerator::visitWasmTrap(LWasmTrap* 
     const MWasmTrap* mir = lir->mir();
 
     masm.jump(trap(mir, mir->trap()));
 }
 
 void
 CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
 {
+#ifdef WASM_HUGE_MEMORY
+    MOZ_CRASH("No wasm bounds check for huge memory");
+#else
     const MWasmBoundsCheck* mir = ins->mir();
     Register ptr = ToRegister(ins->ptr());
-    masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr, trap(mir, wasm::Trap::OutOfBounds));
+    Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit());
+    masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr, boundsCheckLimit,
+                         trap(mir, wasm::Trap::OutOfBounds));
+#endif
+}
+
+void
+CodeGenerator::visitWasmLoadTls(LWasmLoadTls* ins)
+{
+    switch (ins->mir()->type()) {
+      case MIRType::Pointer:
+        masm.loadPtr(Address(ToRegister(ins->tlsPtr()), ins->mir()->offset()),
+                     ToRegister(ins->output()));
+        break;
+      case MIRType::Int32:
+        masm.load32(Address(ToRegister(ins->tlsPtr()), ins->mir()->offset()),
+                    ToRegister(ins->output()));
+        break;
+      default:
+        MOZ_CRASH("MIRType not supported in WasmLoadTls");
+    }
 }
 
 typedef bool (*RecompileFn)(JSContext*);
 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile, "Recompile");
 
 typedef bool (*ForcedRecompileFn)(JSContext*);
 static const VMFunction ForcedRecompileFnInfo =
     FunctionInfo<ForcedRecompileFn>(ForcedRecompile, "ForcedRecompile");
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -432,16 +432,17 @@ class CodeGenerator final : public CodeG
     void visitAssertResultV(LAssertResultV* ins);
     void visitAssertResultT(LAssertResultT* ins);
     void emitAssertResultV(const ValueOperand output, const TemporaryTypeSet* typeset);
     void emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset);
 
     void visitInterruptCheck(LInterruptCheck* lir);
     void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins);
     void visitWasmTrap(LWasmTrap* lir);
+    void visitWasmLoadTls(LWasmLoadTls* ins);
     void visitWasmBoundsCheck(LWasmBoundsCheck* ins);
     void visitRecompileCheck(LRecompileCheck* ins);
     void visitRotate(LRotate* ins);
 
     void visitRandom(LRandom* ins);
     void visitSignExtend(LSignExtend* ins);
 
 #ifdef DEBUG
--- a/js/src/jit/IonControlFlow.cpp
+++ b/js/src/jit/IonControlFlow.cpp
@@ -932,17 +932,17 @@ ControlFlowGenerator::processWhileOrForI
     // Verify that the IFNE goes back to a loophead op.
     MOZ_ASSERT(JSOp(*GetNextPc(pc)) == JSOP_LOOPHEAD);
     MOZ_ASSERT(GetNextPc(pc) == ifne + GetJumpOffset(ifne));
 
     jsbytecode* loopEntry = pc + GetJumpOffset(pc);
 
     size_t stackPhiCount;
     if (SN_TYPE(sn) == SRC_FOR_OF)
-        stackPhiCount = 2;
+        stackPhiCount = 3;
     else if (SN_TYPE(sn) == SRC_FOR_IN)
         stackPhiCount = 1;
     else
         stackPhiCount = 0;
 
     // Skip past the JSOP_LOOPHEAD for the body start.
     jsbytecode* loopHead = GetNextPc(pc);
     jsbytecode* bodyStart = GetNextPc(loopHead);
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -869,12 +869,20 @@ enum class BarrierKind : uint32_t {
 
     // Check if the value is in the TypeSet, including the object type if it's
     // an object.
     TypeSet
 };
 
 enum ReprotectCode { Reprotect = true, DontReprotect = false };
 
+// Rounding modes for round instructions.
+enum class RoundingMode {
+    Down,
+    Up,
+    NearestTiesToEven,
+    TowardsZero
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonTypes_h */
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1428,16 +1428,32 @@ LIRGenerator::visitRound(MRound* ins)
     else
         lir = new (alloc()) LRoundF(useRegister(ins->input()), tempFloat32());
 
     assignSnapshot(lir, Bailout_Round);
     define(lir, ins);
 }
 
 void
+LIRGenerator::visitNearbyInt(MNearbyInt* ins)
+{
+    MIRType inputType = ins->input()->type();
+    MOZ_ASSERT(IsFloatingPointType(inputType));
+    MOZ_ASSERT(ins->type() == inputType);
+
+    LInstructionHelper<1, 1, 0>* lir;
+    if (inputType == MIRType::Double)
+        lir = new(alloc()) LNearbyInt(useRegisterAtStart(ins->input()));
+    else
+        lir = new(alloc()) LNearbyIntF(useRegisterAtStart(ins->input()));
+
+    define(lir, ins);
+}
+
+void
 LIRGenerator::visitMinMax(MMinMax* ins)
 {
     MDefinition* first = ins->getOperand(0);
     MDefinition* second = ins->getOperand(1);
 
     ReorderCommutative(&first, &second, ins);
 
     LMinMaxBase* lir;
@@ -4250,28 +4266,48 @@ void
 LIRGenerator::visitWasmAddOffset(MWasmAddOffset* ins)
 {
     MOZ_ASSERT(ins->base()->type() == MIRType::Int32);
     MOZ_ASSERT(ins->type() == MIRType::Int32);
     define(new(alloc()) LWasmAddOffset(useRegisterAtStart(ins->base())), ins);
 }
 
 void
+LIRGenerator::visitWasmLoadTls(MWasmLoadTls* ins)
+{
+#ifdef WASM_HUGE_MEMORY
+    // This will disappear once we remove HeapReg and replace it with a load
+    // from Tls, but in the mean time it keeps us sane.
+    MOZ_CRASH("No WasmLoadTls here at the moment");
+#endif
+    auto* lir = new(alloc()) LWasmLoadTls(useRegisterAtStart(ins->tlsPtr()));
+    define(lir, ins);
+}
+
+void
 LIRGenerator::visitWasmBoundsCheck(MWasmBoundsCheck* ins)
 {
+#ifdef WASM_HUGE_MEMORY
+    MOZ_CRASH("No bounds checking on huge memory");
+#else
     if (ins->isRedundant()) {
         if (MOZ_LIKELY(!JitOptions.wasmAlwaysCheckBounds))
             return;
     }
 
-    MDefinition* input = ins->input();
-    MOZ_ASSERT(input->type() == MIRType::Int32);
-
-    auto* lir = new(alloc()) LWasmBoundsCheck(useRegisterAtStart(input));
+    MDefinition* index = ins->index();
+    MOZ_ASSERT(index->type() == MIRType::Int32);
+
+    MDefinition* boundsCheckLimit = ins->boundsCheckLimit();
+    MOZ_ASSERT(boundsCheckLimit->type() == MIRType::Int32);
+
+    auto* lir = new(alloc()) LWasmBoundsCheck(useRegisterAtStart(index),
+                                              useRegisterAtStart(boundsCheckLimit));
     add(lir, ins);
+#endif
 }
 
 void
 LIRGenerator::visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins)
 {
     if (ins->type() == MIRType::Int64) {
 #ifdef JS_PUNBOX64
         LAllocation tlsPtr = useRegisterAtStart(ins->tlsPtr());
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -130,16 +130,17 @@ class LIRGenerator : public LIRGenerator
     void visitLsh(MLsh* ins);
     void visitRsh(MRsh* ins);
     void visitUrsh(MUrsh* ins);
     void visitSignExtend(MSignExtend* ins);
     void visitRotate(MRotate* ins);
     void visitFloor(MFloor* ins);
     void visitCeil(MCeil* ins);
     void visitRound(MRound* ins);
+    void visitNearbyInt(MNearbyInt* ins);
     void visitMinMax(MMinMax* ins);
     void visitAbs(MAbs* ins);
     void visitClz(MClz* ins);
     void visitCtz(MCtz* ins);
     void visitSqrt(MSqrt* ins);
     void visitPopcnt(MPopcnt* ins);
     void visitAtan2(MAtan2* ins);
     void visitHypot(MHypot* ins);
@@ -287,16 +288,17 @@ class LIRGenerator : public LIRGenerator
     void visitInArray(MInArray* ins);
     void visitInstanceOf(MInstanceOf* ins);
     void visitCallInstanceOf(MCallInstanceOf* ins);
     void visitIsCallable(MIsCallable* ins);
     void visitIsConstructor(MIsConstructor* ins);
     void visitIsObject(MIsObject* ins);
     void visitHasClass(MHasClass* ins);
     void visitWasmAddOffset(MWasmAddOffset* ins);
+    void visitWasmLoadTls(MWasmLoadTls* ins);
     void visitWasmBoundsCheck(MWasmBoundsCheck* ins);
     void visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins);
     void visitWasmStoreGlobalVar(MWasmStoreGlobalVar* ins);
     void visitWasmParameter(MWasmParameter* ins);
     void visitWasmReturn(MWasmReturn* ins);
     void visitWasmReturnVoid(MWasmReturnVoid* ins);
     void visitWasmStackArg(MWasmStackArg* ins);
     void visitWasmCall(MWasmCall* ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -981,30 +981,40 @@ IonBuilder::inlineMathFloor(CallInfo& ca
         // fully truncated.
         MLimitedTruncate* ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
                                                       MDefinition::IndirectTruncate);
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
     }
 
-    if (IsFloatingPointType(argType) && returnType == MIRType::Int32) {
-        callInfo.setImplicitlyUsedUnchecked();
-        MFloor* ins = MFloor::New(alloc(), callInfo.getArg(0));
-        current->add(ins);
-        current->push(ins);
-        return InliningStatus_Inlined;
-    }
-
-    if (IsFloatingPointType(argType) && returnType == MIRType::Double) {
-        callInfo.setImplicitlyUsedUnchecked();
-        MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor, nullptr);
-        current->add(ins);
-        current->push(ins);
-        return InliningStatus_Inlined;
+    if (IsFloatingPointType(argType)) {
+        if (returnType == MIRType::Int32) {
+            callInfo.setImplicitlyUsedUnchecked();
+            MFloor* ins = MFloor::New(alloc(), callInfo.getArg(0));
+            current->add(ins);
+            current->push(ins);
+            return InliningStatus_Inlined;
+        }
+
+        if (returnType == MIRType::Double) {
+            callInfo.setImplicitlyUsedUnchecked();
+
+            MInstruction* ins = nullptr;
+            if (MNearbyInt::HasAssemblerSupport(RoundingMode::Down)) {
+                ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType, RoundingMode::Down);
+            } else {
+                ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor,
+                                         /* cache */ nullptr);
+            }
+
+            current->add(ins);
+            current->push(ins);
+            return InliningStatus_Inlined;
+        }
     }
 
     return InliningStatus_NotInlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineMathCeil(CallInfo& callInfo)
 {
@@ -1025,30 +1035,40 @@ IonBuilder::inlineMathCeil(CallInfo& cal
         // fully truncated.
         MLimitedTruncate* ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
                                                       MDefinition::IndirectTruncate);
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
     }
 
-    if (IsFloatingPointType(argType) && returnType == MIRType::Int32) {
-        callInfo.setImplicitlyUsedUnchecked();
-        MCeil* ins = MCeil::New(alloc(), callInfo.getArg(0));
-        current->add(ins);
-        current->push(ins);
-        return InliningStatus_Inlined;
-    }
-
-    if (IsFloatingPointType(argType) && returnType == MIRType::Double) {
-        callInfo.setImplicitlyUsedUnchecked();
-        MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil, nullptr);
-        current->add(ins);
-        current->push(ins);
-        return InliningStatus_Inlined;
+    if (IsFloatingPointType(argType)) {
+        if (returnType == MIRType::Int32) {
+            callInfo.setImplicitlyUsedUnchecked();
+            MCeil* ins = MCeil::New(alloc(), callInfo.getArg(0));
+            current->add(ins);
+            current->push(ins);
+            return InliningStatus_Inlined;
+        }
+
+        if (returnType == MIRType::Double) {
+            callInfo.setImplicitlyUsedUnchecked();
+
+            MInstruction* ins = nullptr;
+            if (MNearbyInt::HasAssemblerSupport(RoundingMode::Up)) {
+                ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType, RoundingMode::Up);
+            } else {
+                ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil,
+                                         /* cache */ nullptr);
+            }
+
+            current->add(ins);
+            current->push(ins);
+            return InliningStatus_Inlined;
+        }
     }
 
     return InliningStatus_NotInlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineMathClz32(CallInfo& callInfo)
 {
@@ -1103,17 +1123,18 @@ IonBuilder::inlineMathRound(CallInfo& ca
         MRound* ins = MRound::New(alloc(), callInfo.getArg(0));
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
     }
 
     if (IsFloatingPointType(argType) && returnType == MIRType::Double) {
         callInfo.setImplicitlyUsedUnchecked();
-        MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round, nullptr);
+        MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round,
+                                                /* cache */ nullptr);
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
     }
 
     return InliningStatus_NotInlined;
 }
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1773,16 +1773,29 @@ MLoadUnboxedScalar::printOpcode(GenericP
 void
 MAssertRange::printOpcode(GenericPrinter& out) const
 {
     MDefinition::printOpcode(out);
     out.put(" ");
     assertedRange()->dump(out);
 }
 
+void MNearbyInt::printOpcode(GenericPrinter& out) const
+{
+    MDefinition::printOpcode(out);
+    const char* roundingModeStr = nullptr;
+    switch (roundingMode_) {
+      case RoundingMode::Up:                roundingModeStr = "(up)"; break;
+      case RoundingMode::Down:              roundingModeStr = "(down)"; break;
+      case RoundingMode::NearestTiesToEven: roundingModeStr = "(nearest ties even)"; break;
+      case RoundingMode::TowardsZero:       roundingModeStr = "(towards zero)"; break;
+    }
+    out.printf(" %s", roundingModeStr);
+}
+
 const char*
 MMathFunction::FunctionName(Function function)
 {
     switch (function) {
       case Log:    return "Log";
       case Sin:    return "Sin";
       case Cos:    return "Cos";
       case Exp:    return "Exp";
@@ -2148,16 +2161,23 @@ MCeil::trySpecializeFloat32(TempAllocato
 void
 MRound::trySpecializeFloat32(TempAllocator& alloc)
 {
     MOZ_ASSERT(type() == MIRType::Int32);
     if (EnsureFloatInputOrConvert(this, alloc))
         specialization_ = MIRType::Float32;
 }
 
+void
+MNearbyInt::trySpecializeFloat32(TempAllocator& alloc)
+{
+    if (EnsureFloatInputOrConvert(this, alloc))
+        specialization_ = MIRType::Float32;
+}
+
 MTableSwitch*
 MTableSwitch::New(TempAllocator& alloc, MDefinition* ins, int32_t low, int32_t high)
 {
     return new(alloc) MTableSwitch(alloc, ins, low, high);
 }
 
 MGoto*
 MGoto::New(TempAllocator& alloc, MBasicBlock* target)
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -380,21 +380,23 @@ class AliasSet {
                                     // a typed array, typed object, or unboxed
                                     // object.
         DynamicSlot       = 1 << 3, // A Value member of obj->slots.
         FixedSlot         = 1 << 4, // A Value member of obj->fixedSlots().
         DOMProperty       = 1 << 5, // A DOM property
         FrameArgument     = 1 << 6, // An argument kept on the stack frame
         WasmGlobalVar     = 1 << 7, // An asm.js/wasm global var
         WasmHeap          = 1 << 8, // An asm.js/wasm heap load
-        TypedArrayLength  = 1 << 9,// A typed array's length
+        WasmHeapMeta      = 1 << 9, // The asm.js/wasm heap base pointer and
+                                    // bounds check limit, in Tls.
+        TypedArrayLength  = 1 << 10,// A typed array's length
         Last              = TypedArrayLength,
         Any               = Last | (Last - 1),
 
-        NumCategories     = 10,
+        NumCategories     = 11,
 
         // Indicates load or store.
         Store_            = 1 << 31
     };
 
     static_assert((1 << NumCategories) - 1 == Any,
                   "NumCategories must include all flags present in Any");
 
@@ -12185,17 +12187,17 @@ class MStringLength
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
 
     ALLOW_CLONE(MStringLength)
 };
 
-// Inlined version of Math.floor().
+// Inlined assembly for Math.floor(double | float32) -> int32.
 class MFloor
   : public MUnaryInstruction,
     public FloatingPointPolicy<0>::Data
 {
     explicit MFloor(MDefinition* num)
       : MUnaryInstruction(num)
     {
         setResultType(MIRType::Int32);
@@ -12226,17 +12228,17 @@ class MFloor
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
 
     ALLOW_CLONE(MFloor)
 };
 
-// Inlined version of Math.ceil().
+// Inlined assembly version for Math.ceil(double | float32) -> int32.
 class MCeil
   : public MUnaryInstruction,
     public FloatingPointPolicy<0>::Data
 {
     explicit MCeil(MDefinition* num)
       : MUnaryInstruction(num)
     {
         setResultType(MIRType::Int32);
@@ -12267,17 +12269,17 @@ class MCeil
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
 
     ALLOW_CLONE(MCeil)
 };
 
-// Inlined version of Math.round().
+// Inlined version of Math.round(double | float32) -> int32.
 class MRound
   : public MUnaryInstruction,
     public FloatingPointPolicy<0>::Data
 {
     explicit MRound(MDefinition* num)
       : MUnaryInstruction(num)
     {
         setResultType(MIRType::Int32);
@@ -12309,16 +12311,71 @@ class MRound
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
 
     ALLOW_CLONE(MRound)
 };
 
+// NearbyInt rounds the floating-point input to the nearest integer, according
+// to the RoundingMode.
+class MNearbyInt
+  : public MUnaryInstruction,
+    public FloatingPointPolicy<0>::Data
+{
+    RoundingMode roundingMode_;
+
+    explicit MNearbyInt(MDefinition* num, MIRType resultType, RoundingMode roundingMode)
+      : MUnaryInstruction(num),
+        roundingMode_(roundingMode)
+    {
+        MOZ_ASSERT(HasAssemblerSupport(roundingMode));
+
+        MOZ_ASSERT(IsFloatingPointType(resultType));
+        setResultType(resultType);
+        specialization_ = resultType;
+
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(NearbyInt)
+    TRIVIAL_NEW_WRAPPERS
+
+    static bool HasAssemblerSupport(RoundingMode mode) {
+        return Assembler::HasRoundInstruction(mode);
+    }
+
+    RoundingMode roundingMode() const { return roundingMode_; }
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+
+    bool isFloat32Commutative() const override {
+        return true;
+    }
+    void trySpecializeFloat32(TempAllocator& alloc) override;
+#ifdef DEBUG
+    bool isConsistentFloat32Use(MUse* use) const override {
+        return true;
+    }
+#endif
+
+    bool congruentTo(const MDefinition* ins) const override {
+        return congruentIfOperandsEqual(ins) &&
+               ins->toNearbyInt()->roundingMode() == roundingMode_;
+    }
+
+    void printOpcode(GenericPrinter& out) const override;
+
+    ALLOW_CLONE(MNearbyInt)
+};
+
 class MIteratorStart
   : public MUnaryInstruction,
     public BoxExceptPolicy<0, MIRType::Object>::Data
 {
     uint8_t flags_;
 
     MIteratorStart(MDefinition* obj, uint8_t flags)
       : MUnaryInstruction(obj), flags_(flags)
@@ -13612,34 +13669,82 @@ class MAsmJSNeg
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSNeg)
     TRIVIAL_NEW_WRAPPERS
 };
 
+class MWasmLoadTls
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
+{
+    uint32_t offset_;
+    AliasSet aliases_;
+
+    explicit MWasmLoadTls(MDefinition* tlsPointer, uint32_t offset, MIRType type, AliasSet aliases)
+      : MUnaryInstruction(tlsPointer),
+        offset_(offset),
+        aliases_(aliases)
+    {
+        // Different Tls data have different alias classes and only those classes are allowed.
+        MOZ_ASSERT(aliases_.flags() == AliasSet::Load(AliasSet::WasmHeapMeta).flags() ||
+                   aliases_.flags() == AliasSet::None().flags());
+
+        // The only types supported at the moment.
+        MOZ_ASSERT(type == MIRType::Pointer || type == MIRType::Int32);
+
+        setMovable();
+        setResultType(type);
+    }
+
+  public:
+    INSTRUCTION_HEADER(WasmLoadTls)
+    TRIVIAL_NEW_WRAPPERS
+    NAMED_OPERANDS((0, tlsPtr))
+
+    uint32_t offset() const {
+        return offset_;
+    }
+
+    bool congruentTo(const MDefinition* ins) const override {
+        return op() == ins->op() &&
+               offset() == ins->toWasmLoadTls()->offset() &&
+               type() == ins->type();
+    }
+
+    HashNumber valueHash() const override {
+        return op() + offset();
+    }
+
+    AliasSet getAliasSet() const override {
+        return aliases_;
+    }
+};
+
 class MWasmBoundsCheck
-  : public MUnaryInstruction,
+  : public MBinaryInstruction,
     public NoTypePolicy::Data
 {
     bool redundant_;
     wasm::TrapOffset trapOffset_;
 
-    explicit MWasmBoundsCheck(MDefinition* index, wasm::TrapOffset trapOffset)
-      : MUnaryInstruction(index),
+    explicit MWasmBoundsCheck(MDefinition* index, MDefinition* boundsCheckLimit, wasm::TrapOffset trapOffset)
+      : MBinaryInstruction(index, boundsCheckLimit),
         redundant_(false),
         trapOffset_(trapOffset)
     {
         setGuard(); // Effectful: throws for OOB.
     }
 
   public:
     INSTRUCTION_HEADER(WasmBoundsCheck)
     TRIVIAL_NEW_WRAPPERS
+    NAMED_OPERANDS((0, index), (1, boundsCheckLimit))
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 
     bool isRedundant() const {
         return redundant_;
     }
@@ -13684,64 +13789,95 @@ class MWasmAddOffset
         return offset_;
     }
     wasm::TrapOffset trapOffset() const {
         return trapOffset_;
     }
 };
 
 class MWasmLoad
-  : public MUnaryInstruction,
+  : public MVariadicInstruction, // memoryBase is nullptr on some platforms
     public NoTypePolicy::Data
 {
     wasm::MemoryAccessDesc access_;
 
-    MWasmLoad(MDefinition* base, const wasm::MemoryAccessDesc& access, MIRType resultType)
-      : MUnaryInstruction(base),
-        access_(access)
+    explicit MWasmLoad(const wasm::MemoryAccessDesc& access, MIRType resultType)
+      : access_(access)
     {
         setGuard();
         setResultType(resultType);
     }
 
   public:
     INSTRUCTION_HEADER(WasmLoad)
-    TRIVIAL_NEW_WRAPPERS
-    NAMED_OPERANDS((0, base))
+    NAMED_OPERANDS((0, base), (1, memoryBase));
+
+    static MWasmLoad* New(TempAllocator& alloc,
+                          MDefinition* memoryBase,
+                          MDefinition* base,
+                          const wasm::MemoryAccessDesc& access,
+                          MIRType resultType)
+    {
+        MWasmLoad* load = new(alloc) MWasmLoad(access, resultType);
+        if (!load->init(alloc, 1 + !!memoryBase))
+            return nullptr;
+
+        load->initOperand(0, base);
+        if (memoryBase)
+            load->initOperand(1, memoryBase);
+
+        return load;
+    }
 
     const wasm::MemoryAccessDesc& access() const {
         return access_;
     }
 
     AliasSet getAliasSet() const override {
         // When a barrier is needed, make the instruction effectful by giving
         // it a "store" effect.
         if (access_.isAtomic())
             return AliasSet::Store(AliasSet::WasmHeap);
         return AliasSet::Load(AliasSet::WasmHeap);
     }
 };
 
 class MWasmStore
-  : public MBinaryInstruction,
+  : public MVariadicInstruction,
     public NoTypePolicy::Data
 {
     wasm::MemoryAccessDesc access_;
 
-    MWasmStore(MDefinition* base, const wasm::MemoryAccessDesc& access, MDefinition* value)
-      : MBinaryInstruction(base, value),
-        access_(access)
+    explicit MWasmStore(const wasm::MemoryAccessDesc& access)
+      : access_(access)
     {
         setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(WasmStore)
-    TRIVIAL_NEW_WRAPPERS
-    NAMED_OPERANDS((0, base), (1, value))
+    NAMED_OPERANDS((0, base), (1, value), (2, memoryBase))
+
+    static MWasmStore* New(TempAllocator& alloc,
+                           MDefinition* memoryBase,
+                           MDefinition* base,
+                           const wasm::MemoryAccessDesc& access,
+                           MDefinition* value)
+    {
+        MWasmStore* store = new(alloc) MWasmStore(access);
+        if (!store->init(alloc, 2 + !!memoryBase))
+            return nullptr;
+
+        store->initOperand(0, base);
+        store->initOperand(1, value);
+        if (memoryBase)
+            store->initOperand(2, memoryBase);
+
+        return store;
+    }
 
     const wasm::MemoryAccessDesc& access() const {
         return access_;
     }
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::WasmHeap);
     }
@@ -13774,152 +13910,272 @@ class MAsmJSMemoryAccess
                                       mozilla::Nothing());
     }
 
     void removeBoundsCheck() { needsBoundsCheck_ = false; }
     void setOffset(uint32_t o) { offset_ = o; }
 };
 
 class MAsmJSLoadHeap
-  : public MUnaryInstruction,
+  : public MVariadicInstruction, // 1 plus optional memoryBase and boundsCheckLimit
     public MAsmJSMemoryAccess,
     public NoTypePolicy::Data
 {
-    MAsmJSLoadHeap(MDefinition* base, Scalar::Type accessType)
-      : MUnaryInstruction(base),
-        MAsmJSMemoryAccess(accessType)
+    uint32_t memoryBaseIndex_;
+    uint32_t boundsCheckIndex_;
+
+    explicit MAsmJSLoadHeap(uint32_t memoryBaseIndex, uint32_t boundsCheckIndex,
+                            Scalar::Type accessType)
+      : MAsmJSMemoryAccess(accessType),
+        memoryBaseIndex_(memoryBaseIndex),
+        boundsCheckIndex_(boundsCheckIndex)
     {
         setResultType(ScalarTypeToMIRType(accessType));
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSLoadHeap)
-    TRIVIAL_NEW_WRAPPERS
+
+    static MAsmJSLoadHeap* New(TempAllocator& alloc,
+                               MDefinition* memoryBase,
+                               MDefinition* base,
+                               MDefinition* boundsCheckLimit,
+                               Scalar::Type accessType)
+    {
+        uint32_t nextIndex = 1;
+        uint32_t memoryBaseIndex = memoryBase ? nextIndex++ : UINT32_MAX;
+        uint32_t boundsCheckIndex = boundsCheckLimit ? nextIndex++ : UINT32_MAX;
+
+        MAsmJSLoadHeap* load = new(alloc) MAsmJSLoadHeap(memoryBaseIndex, boundsCheckIndex,
+                                                         accessType);
+        if (!load->init(alloc, nextIndex))
+            return nullptr;
+
+        load->initOperand(0, base);
+        if (memoryBase)
+            load->initOperand(memoryBaseIndex, memoryBase);
+        if (boundsCheckLimit)
+            load->initOperand(boundsCheckIndex, boundsCheckLimit);
+
+        return load;
+    }
 
     MDefinition* base() const { return getOperand(0); }
     void replaceBase(MDefinition* newBase) { replaceOperand(0, newBase); }
+    MDefinition* memoryBase() const { return getOperand(memoryBaseIndex_); }
+    MDefinition* boundsCheckLimit() const { return getOperand(boundsCheckIndex_); }
 
     bool congruentTo(const MDefinition* ins) const override;
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::WasmHeap);
     }
     AliasType mightAlias(const MDefinition* def) const override;
 };
 
 class MAsmJSStoreHeap
-  : public MBinaryInstruction,
+  : public MVariadicInstruction, // 2 plus optional memoryBase and boundsCheckLimit
     public MAsmJSMemoryAccess,
     public NoTypePolicy::Data
 {
-    MAsmJSStoreHeap(MDefinition* base, Scalar::Type accessType, MDefinition* v)
-      : MBinaryInstruction(base, v),
-        MAsmJSMemoryAccess(accessType)
-    {}
+    uint32_t memoryBaseIndex_;
+    uint32_t boundsCheckIndex_;
+
+    explicit MAsmJSStoreHeap(uint32_t memoryBaseIndex, uint32_t boundsCheckIndex,
+                             Scalar::Type accessType)
+      : MAsmJSMemoryAccess(accessType),
+        memoryBaseIndex_(memoryBaseIndex),
+        boundsCheckIndex_(boundsCheckIndex)
+    {
+    }
 
   public:
     INSTRUCTION_HEADER(AsmJSStoreHeap)
-    TRIVIAL_NEW_WRAPPERS
+
+    static MAsmJSStoreHeap* New(TempAllocator& alloc,
+                                MDefinition* memoryBase,
+                                MDefinition* base,
+                                MDefinition* boundsCheckLimit,
+                                Scalar::Type accessType,
+                                MDefinition* v)
+    {
+        uint32_t nextIndex = 2;
+        uint32_t memoryBaseIndex = memoryBase ? nextIndex++ : UINT32_MAX;
+        uint32_t boundsCheckIndex = boundsCheckLimit ? nextIndex++ : UINT32_MAX;
+
+        MAsmJSStoreHeap* store = new(alloc) MAsmJSStoreHeap(memoryBaseIndex, boundsCheckIndex,
+                                                            accessType);
+        if (!store->init(alloc, nextIndex))
+            return nullptr;
+
+        store->initOperand(0, base);
+        store->initOperand(1, v);
+        if (memoryBase)
+            store->initOperand(memoryBaseIndex, memoryBase);
+        if (boundsCheckLimit)
+            store->initOperand(boundsCheckIndex, boundsCheckLimit);
+
+        return store;
+    }
 
     MDefinition* base() const { return getOperand(0); }
     void replaceBase(MDefinition* newBase) { replaceOperand(0, newBase); }
     MDefinition* value() const { return getOperand(1); }
+    MDefinition* memoryBase() const { return getOperand(memoryBaseIndex_); }
+    MDefinition* boundsCheckLimit() const { return getOperand(boundsCheckIndex_); }
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::WasmHeap);
     }
 };
 
 class MAsmJSCompareExchangeHeap
-  : public MQuaternaryInstruction,
+  : public MVariadicInstruction,
     public NoTypePolicy::Data
 {
     wasm::MemoryAccessDesc access_;
 
-    MAsmJSCompareExchangeHeap(MDefinition* base, const wasm::MemoryAccessDesc& access,
-                              MDefinition* oldv, MDefinition* newv, MDefinition* tls)
-        : MQuaternaryInstruction(base, oldv, newv, tls),
-          access_(access)
+    explicit MAsmJSCompareExchangeHeap(const wasm::MemoryAccessDesc& access)
+      : access_(access)
     {
         setGuard();             // Not removable
         setResultType(MIRType::Int32);
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSCompareExchangeHeap)
-    TRIVIAL_NEW_WRAPPERS
+
+    static MAsmJSCompareExchangeHeap* New(TempAllocator& alloc,
+                                          MDefinition* memoryBase,
+                                          MDefinition* base,
+                                          const wasm::MemoryAccessDesc& access,
+                                          MDefinition* oldv,
+                                          MDefinition* newv,
+                                          MDefinition* tls)
+    {
+        MAsmJSCompareExchangeHeap* cas = new(alloc) MAsmJSCompareExchangeHeap(access);
+        if (!cas->init(alloc, 4 + !!memoryBase))
+            return nullptr;
+
+        cas->initOperand(0, base);
+        cas->initOperand(1, oldv);
+        cas->initOperand(2, newv);
+        cas->initOperand(3, tls);
+        if (memoryBase)
+            cas->initOperand(4, memoryBase);
+
+        return cas;
+    }
 
     const wasm::MemoryAccessDesc& access() const { return access_; }
 
     MDefinition* base() const { return getOperand(0); }
     MDefinition* oldValue() const { return getOperand(1); }
     MDefinition* newValue() const { return getOperand(2); }
     MDefinition* tls() const { return getOperand(3); }
+    MDefinition* memoryBase() const { return getOperand(4); }
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::WasmHeap);
     }
 };
 
 class MAsmJSAtomicExchangeHeap
-  : public MTernaryInstruction,
+  : public MVariadicInstruction,
     public NoTypePolicy::Data
 {
     wasm::MemoryAccessDesc access_;
 
-    MAsmJSAtomicExchangeHeap(MDefinition* base, const wasm::MemoryAccessDesc& access,
-                             MDefinition* value, MDefinition* tls)
-        : MTernaryInstruction(base, value, tls),
-          access_(access)
+    explicit MAsmJSAtomicExchangeHeap(const wasm::MemoryAccessDesc& access)
+        : access_(access)
     {
         setGuard();             // Not removable
         setResultType(MIRType::Int32);
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSAtomicExchangeHeap)
-    TRIVIAL_NEW_WRAPPERS
+
+    static MAsmJSAtomicExchangeHeap* New(TempAllocator& alloc,
+                                         MDefinition* memoryBase,
+                                         MDefinition* base,
+                                         const wasm::MemoryAccessDesc& access,
+                                         MDefinition* value,
+                                         MDefinition* tls)
+    {
+        MAsmJSAtomicExchangeHeap* xchg = new(alloc) MAsmJSAtomicExchangeHeap(access);
+        if (!xchg->init(alloc, 3 + !!memoryBase))
+            return nullptr;
+
+        xchg->initOperand(0, base);
+        xchg->initOperand(1, value);
+        xchg->initOperand(2, tls);
+        if (memoryBase)
+            xchg->initOperand(3, memoryBase);
+
+        return xchg;
+    }
 
     const wasm::MemoryAccessDesc& access() const { return access_; }
 
     MDefinition* base() const { return getOperand(0); }
     MDefinition* value() const { return getOperand(1); }
     MDefinition* tls() const { return getOperand(2); }
+    MDefinition* memoryBase() const { return getOperand(3); }
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::WasmHeap);
     }
 };
 
 class MAsmJSAtomicBinopHeap
-  : public MTernaryInstruction,
+  : public MVariadicInstruction,
     public NoTypePolicy::Data
 {
     AtomicOp op_;
     wasm::MemoryAccessDesc access_;
 
-    MAsmJSAtomicBinopHeap(AtomicOp op, MDefinition* base, const wasm::MemoryAccessDesc& access,
-                          MDefinition* v, MDefinition* tls)
-        : MTernaryInstruction(base, v, tls),
-          op_(op),
+    explicit MAsmJSAtomicBinopHeap(AtomicOp op, const wasm::MemoryAccessDesc& access)
+        : op_(op),
           access_(access)
     {
         setGuard();         // Not removable
         setResultType(MIRType::Int32);
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSAtomicBinopHeap)
-    TRIVIAL_NEW_WRAPPERS
+
+    static MAsmJSAtomicBinopHeap* New(TempAllocator& alloc,
+                                      AtomicOp op,
+                                      MDefinition* memoryBase,
+                                      MDefinition* base,
+                                      const wasm::MemoryAccessDesc& access,
+                                      MDefinition* v,
+                                      MDefinition* tls)
+    {
+        MAsmJSAtomicBinopHeap* binop = new(alloc) MAsmJSAtomicBinopHeap(op, access);
+        if (!binop->init(alloc, 3 + !!memoryBase))
+            return nullptr;
+
+        binop->initOperand(0, base);
+        binop->initOperand(1, v);
+        binop->initOperand(2, tls);
+        if (memoryBase)
+            binop->initOperand(3, memoryBase);
+
+        return binop;
+    }
 
     AtomicOp operation() const { return op_; }
     const wasm::MemoryAccessDesc& access() const { return access_; }
 
     MDefinition* base() const { return getOperand(0); }
     MDefinition* value() const { return getOperand(1); }
     MDefinition* tls() const { return getOperand(2); }
+    MDefinition* memoryBase() const { return getOperand(3); }
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::WasmHeap);
     }
 };
 
 class MWasmLoadGlobalVar
   : public MAryInstruction<1>,
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -258,16 +258,17 @@ namespace jit {
     _(ArgumentsLength)                                                      \
     _(GetFrameArgument)                                                     \
     _(SetFrameArgument)                                                     \
     _(RunOncePrologue)                                                      \
     _(Rest)                                                                 \
     _(Floor)                                                                \
     _(Ceil)                                                                 \
     _(Round)                                                                \
+    _(NearbyInt)                                                            \
     _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
     _(GetDOMProperty)                                                       \
     _(GetDOMMember)                                                         \
     _(SetDOMProperty)                                                       \
     _(IsConstructor)                                                        \
@@ -292,16 +293,17 @@ namespace jit {
     _(DebugCheckSelfHosted)                                                 \
     _(AsmJSNeg)                                                             \
     _(AsmJSLoadHeap)                                                        \
     _(AsmJSStoreHeap)                                                       \
     _(AsmJSCompareExchangeHeap)                                             \
     _(AsmJSAtomicExchangeHeap)                                              \
     _(AsmJSAtomicBinopHeap)                                                 \
     _(WasmBoundsCheck)                                                      \
+    _(WasmLoadTls)                                                          \
     _(WasmAddOffset)                                                        \
     _(WasmLoad)                                                             \
     _(WasmStore)                                                            \
     _(WasmTrap)                                                             \
     _(WasmTruncateToInt32)                                                  \
     _(WasmUnsignedToDouble)                                                 \
     _(WasmUnsignedToFloat32)                                                \
     _(WasmLoadGlobalVar)                                                    \
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1355,24 +1355,25 @@ class MacroAssembler : public MacroAssem
     inline void truncateDoubleToUInt64(Address src, Address dest, Register temp,
                                        FloatRegister floatTemp)
         DEFINED_ON(x86, x64);
 
   public:
     // ========================================================================
     // wasm support
 
-    // Emit a bounds check against the (dynamically-patched) wasm bounds check
-    // limit, jumping to 'label' if 'cond' holds.
+    // Emit a bounds check against the wasm heap limit, jumping to 'label' if 'cond' holds.
+    // Required when WASM_HUGE_MEMORY is not defined.
     template <class L>
-    inline void wasmBoundsCheck(Condition cond, Register index, L label) PER_ARCH;
+    inline void wasmBoundsCheck(Condition cond, Register index, Register boundsCheckLimit, L label)
+        DEFINED_ON(arm, arm64, mips32, mips64, x86);
 
-    // Called after compilation completes to patch the given limit into the
-    // given instruction's immediate.
-    static inline void wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit) PER_ARCH;
+    template <class L>
+    inline void wasmBoundsCheck(Condition cond, Register index, Address boundsCheckLimit, L label)
+        DEFINED_ON(arm, arm64, mips32, mips64, x86);
 
     // On x86, each instruction adds its own wasm::MemoryAccess's to the
     // wasm::MemoryAccessVector (there can be multiple when i64 is involved).
     // On x64, only some asm.js accesses need a wasm::MemoryAccess so the caller
     // is responsible for doing this instead.
     void wasmLoad(const wasm::MemoryAccessDesc& access, Operand srcAddr, AnyRegister out) DEFINED_ON(x86, x64);
     void wasmLoadI64(const wasm::MemoryAccessDesc& access, Operand srcAddr, Register64 out) DEFINED_ON(x86, x64);
     void wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value, Operand dstAddr) DEFINED_ON(x86, x64);
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1742,16 +1742,18 @@ class Assembler : public AssemblerShared
     }
     static bool SupportsUnalignedAccesses() {
         return HasARMv7();
     }
     static bool SupportsSimd() {
         return js::jit::SupportsSimd;
     }
 
+    static bool HasRoundInstruction(RoundingMode mode) { return false; }
+
   protected:
     void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) {
         enoughMemory_ &= jumps_.append(RelativePatch(target.value, kind));
         if (kind == Relocation::JITCODE)
             writeRelocation(src);
     }
 
   public:
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -2287,16 +2287,17 @@ CodeGeneratorARM::visitWasmCallI64(LWasm
 
 void
 CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
 {
     const MAsmJSLoadHeap* mir = ins->mir();
     MOZ_ASSERT(mir->offset() == 0);
 
     const LAllocation* ptr = ins->ptr();
+    const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
 
     bool isSigned;
     int size;
     bool isFloat = false;
     switch (mir->accessType()) {
       case Scalar::Int8:    isSigned = true;  size =  8; break;
       case Scalar::Uint8:   isSigned = false; size =  8; break;
       case Scalar::Int16:   isSigned = true;  size = 16; break;
@@ -2320,47 +2321,47 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAs
             else
                 masm.ma_vldr(Address(HeapReg, ptrImm), vd, scratch, Assembler::Always);
         }  else {
             ScratchRegisterScope scratch(masm);
             masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm),
                                   ToRegister(ins->output()), scratch, Offset, Assembler::Always);
         }
     } else {
-        ScratchRegisterScope scratch(masm);
         Register ptrReg = ToRegister(ptr);
         if (isFloat) {
             FloatRegister output = ToFloatRegister(ins->output());
             if (size == 32)
                 output = output.singleOverlay();
 
             Assembler::Condition cond = Assembler::Always;
             if (mir->needsBoundsCheck()) {
-                BufferOffset cmp = masm.as_cmp(ptrReg, Imm8(0));
-                masm.append(wasm::BoundsCheck(cmp.getOffset()));
+                Register boundsCheckLimitReg = ToRegister(boundsCheckLimit);
+                masm.as_cmp(ptrReg, O2Reg(boundsCheckLimitReg));
                 if (size == 32)
                     masm.ma_vimm_f32(GenericNaN(), output, Assembler::AboveOrEqual);
                 else
                     masm.ma_vimm(GenericNaN(), output, Assembler::AboveOrEqual);
                 cond = Assembler::Below;
             }
 
+            ScratchRegisterScope scratch(masm);
             masm.ma_vldr(output, HeapReg, ptrReg, scratch, 0, cond);
         } else {
             Register output = ToRegister(ins->output());
 
             Assembler::Condition cond = Assembler::Always;
             if (mir->needsBoundsCheck()) {
-                uint32_t cmpOffset = masm.as_cmp(ptrReg, Imm8(0)).getOffset();
-                masm.append(wasm::BoundsCheck(cmpOffset));
-
+                Register boundsCheckLimitReg = ToRegister(boundsCheckLimit);
+                masm.as_cmp(ptrReg, O2Reg(boundsCheckLimitReg));
                 masm.ma_mov(Imm32(0), output, Assembler::AboveOrEqual);
                 cond = Assembler::Below;
             }
 
+            ScratchRegisterScope scratch(masm);
             masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, output, scratch, Offset, cond);
         }
     }
 }
 
 template <typename T>
 void
 CodeGeneratorARM::emitWasmLoad(T* lir)
@@ -2512,16 +2513,17 @@ CodeGeneratorARM::visitWasmUnalignedStor
 
 void
 CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
 {
     const MAsmJSStoreHeap* mir = ins->mir();
     MOZ_ASSERT(mir->offset() == 0);
 
     const LAllocation* ptr = ins->ptr();
+    const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
 
     bool isSigned;
     int size;
     bool isFloat = false;
     switch (mir->accessType()) {
       case Scalar::Int8:
       case Scalar::Uint8:   isSigned = false; size = 8; break;
       case Scalar::Int16:
@@ -2549,19 +2551,18 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
                                   ToRegister(ins->value()), scratch, Offset, Assembler::Always);
         }
     } else {
         Register ptrReg = ToRegister(ptr);
 
         Assembler::Condition cond = Assembler::Always;
         if (mir->needsBoundsCheck()) {
-            BufferOffset cmp = masm.as_cmp(ptrReg, Imm8(0));
-            masm.append(wasm::BoundsCheck(cmp.getOffset()));
-
+            Register boundsCheckLimitReg = ToRegister(boundsCheckLimit);
+            masm.as_cmp(ptrReg, O2Reg(boundsCheckLimitReg));
             cond = Assembler::Below;
         }
 
         if (isFloat) {
             ScratchRegisterScope scratch(masm);
             FloatRegister value = ToFloatRegister(ins->value());
             if (size == 32)
                 value = value.singleOverlay();
--- a/js/src/jit/arm/LIR-arm.h
+++ b/js/src/jit/arm/LIR-arm.h
@@ -607,17 +607,17 @@ namespace details {
 template<size_t NumDefs>
 class LWasmUnalignedLoadBase : public details::LWasmLoadBase<NumDefs, 4>
 {
   public:
     typedef LWasmLoadBase<NumDefs, 4> Base;
     explicit LWasmUnalignedLoadBase(const LAllocation& ptr, const LDefinition& ptrCopy,
                                     const LDefinition& temp1, const LDefinition& temp2,
                                     const LDefinition& temp3)
-      : Base(ptr)
+      : Base(ptr, LAllocation())
     {
         Base::setTemp(0, ptrCopy);
         Base::setTemp(1, temp1);
         Base::setTemp(2, temp2);
         Base::setTemp(3, temp3);
     }
 
     const LDefinition* ptrCopy() {
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -707,44 +707,59 @@ LIRGeneratorARM::visitAsmJSLoadHeap(MAsm
 {
     MOZ_ASSERT(ins->offset() == 0);
 
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
     // For the ARM it is best to keep the 'base' in a register if a bounds check is needed.
     LAllocation baseAlloc;
+    LAllocation limitAlloc;
+
     if (base->isConstant() && !ins->needsBoundsCheck()) {
         // A bounds check is only skipped for a positive index.
         MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
         baseAlloc = LAllocation(base->toConstant());
     } else {
         baseAlloc = useRegisterAtStart(base);
+        if (ins->needsBoundsCheck()) {
+            MDefinition* boundsCheckLimit = ins->boundsCheckLimit();
+            MOZ_ASSERT(boundsCheckLimit->type() == MIRType::Int32);
+            limitAlloc = useRegisterAtStart(boundsCheckLimit);
+        }
     }
 
-    define(new(alloc()) LAsmJSLoadHeap(baseAlloc), ins);
+    define(new(alloc()) LAsmJSLoadHeap(baseAlloc, limitAlloc), ins);
 }
 
 void
 LIRGeneratorARM::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
 {
     MOZ_ASSERT(ins->offset() == 0);
 
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
+
     LAllocation baseAlloc;
+    LAllocation limitAlloc;
 
     if (base->isConstant() && !ins->needsBoundsCheck()) {
         MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
         baseAlloc = LAllocation(base->toConstant());
     } else {
         baseAlloc = useRegisterAtStart(base);
+        if (ins->needsBoundsCheck()) {
+            MDefinition* boundsCheckLimit = ins->boundsCheckLimit();
+            MOZ_ASSERT(boundsCheckLimit->type() == MIRType::Int32);
+            limitAlloc = useRegisterAtStart(boundsCheckLimit);
+        }
     }
 
-    add(new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value())), ins);
+    add(new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()), limitAlloc),
+        ins);
 }
 
 void
 LIRGeneratorARM::lowerTruncateDToInt32(MTruncateToInt32* ins)
 {
     MDefinition* opd = ins->input();
     MOZ_ASSERT(opd->type() == MIRType::Double);
 
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -2122,41 +2122,31 @@ MacroAssembler::clampIntToUint8(Register
     ma_mov(Imm32(0), reg, Signed);
 }
 
 // ========================================================================
 // wasm support
 
 template <class L>
 void
-MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Register boundsCheckLimit, L label)
 {
-    BufferOffset bo = as_cmp(index, Imm8(0));
-    append(wasm::BoundsCheck(bo.getOffset()));
-
+    as_cmp(index, O2Reg(boundsCheckLimit));
     as_b(label, cond);
 }
 
+template <class L>
 void
-MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Address boundsCheckLimit, L label)
 {
-    Instruction* inst = (Instruction*) patchAt;
-    MOZ_ASSERT(inst->is<InstCMP>());
-    InstCMP* cmp = inst->as<InstCMP>();
-
-    Register index;
-    cmp->extractOp1(&index);
-
-    MOZ_ASSERT(cmp->extractOp2().isImm8());
-
-    Imm8 imm8 = Imm8(limit);
-    MOZ_RELEASE_ASSERT(!imm8.invalid());
-
-    *inst = InstALU(InvalidReg, index, imm8, OpCmp, SetCC, Always);
-    // Don't call Auto Flush Cache; the wasm caller has done this for us.
+    ScratchRegisterScope scratch(*this);
+    MOZ_ASSERT(boundsCheckLimit.offset == offsetof(wasm::TlsData, boundsCheckLimit));
+    ma_ldr(DTRAddr(boundsCheckLimit.base, DtrOffImm(boundsCheckLimit.offset)), scratch);
+    as_cmp(index, O2Reg(scratch));
+    as_b(label, cond);
 }
 
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerARMCompat::incrementInt32Value(const Address& addr)
 {
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -292,16 +292,18 @@ class Assembler : public vixl::Assembler
     }
     void setPrinter(Sprinter* sp) {
     }
 
     static bool SupportsFloatingPoint() { return true; }
     static bool SupportsUnalignedAccesses() { return true; }
     static bool SupportsSimd() { return js::jit::SupportsSimd; }
 
+    static bool HasRoundInstruction(RoundingMode mode) { return false; }
+
     // Tracks a jump that is patchable after finalization.
     void addJumpRelocation(BufferOffset src, Relocation::Kind reloc);
 
   protected:
     // Add a jump whose target is unknown until finalization.
     // The jump may not be patched at runtime.
     void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind);
 
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -1703,23 +1703,24 @@ MacroAssembler::clampIntToUint8(Register
     Csel(reg32, reg32, scratch32, Assembler::LessThanOrEqual);
 }
 
 // ========================================================================
 // wasm support
 
 template <class L>
 void
-MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Register boundsCheckLimit, L label)
 {
     MOZ_CRASH("NYI");
 }
 
+template <class L>
 void
-MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Address boundsCheckLimit, L label)
 {
     MOZ_CRASH("NYI");
 }
 
 //}}} check_macroassembler_style
 // ===============================================================
 
 template <typename T>
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -1233,16 +1233,20 @@ class AssemblerMIPSShared : public Assem
     }
     static bool SupportsUnalignedAccesses() {
         return true;
     }
     static bool SupportsSimd() {
         return js::jit::SupportsSimd;
     }
 
+    static bool HasRoundInstruction(RoundingMode mode) {
+        return false;
+    }
+
   protected:
     InstImm invertBranch(InstImm branch, BOffImm16 skipOffset);
     void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) {
         enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind));
         if (kind == Relocation::JITCODE)
             writeRelocation(src);
     }
 
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
@@ -2124,17 +2124,17 @@ CodeGeneratorMIPSShared::visitAsmJSLoadH
             masm.loadConstantFloat32(float(GenericNaN()), ToFloatRegister(out));
         else
             masm.loadConstantDouble(GenericNaN(), ToFloatRegister(out));
     } else {
         masm.move32(Imm32(0), ToRegister(out));
     }
     masm.bind(&done);
 
-    masm.append(wasm::BoundsCheck(bo.getOffset()));
+    MOZ_CRASH("NYI - patching is no longer an option");
 }
 
 void
 CodeGeneratorMIPSShared::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
 {
     const MAsmJSStoreHeap* mir = ins->mir();
     const LAllocation* value = ins->value();
     const LAllocation* ptr = ins->ptr();
@@ -2203,17 +2203,17 @@ CodeGeneratorMIPSShared::visitAsmJSStore
         } else
             masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
     } else {
         masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne),
                       static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
     }
 
     masm.bind(&outOfRange);
-    masm.append(wasm::BoundsCheck(bo.getOffset()));
+    MOZ_CRASH("NYI - patching is no longer an option");
 }
 
 void
 CodeGeneratorMIPSShared::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
 {
     MAsmJSCompareExchangeHeap* mir = ins->mir();
     Scalar::Type vt = mir->access().type();
     const LAllocation* ptr = ins->ptr();
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -1033,33 +1033,30 @@ MacroAssembler::storeUncanonicalizedFloa
     ma_ss(src, addr);
 }
 
 // ========================================================================
 // wasm support
 
 template <class L>
 void
-MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Register boundsCheckLimit, L label)
 {
-    BufferOffset bo = ma_BoundsCheck(ScratchRegister);
-    append(wasm::BoundsCheck(bo.getOffset()));
+    MOZ_CRASH("NYI - patching is no longer available");
+    // BufferOffset bo = ma_BoundsCheck(ScratchRegister);
+    // append(wasm::BoundsCheck(bo.getOffset()));
 
-    ma_b(index, ScratchRegister, label, cond);
+    // ma_b(index, ScratchRegister, label, cond);
 }
 
+template <class L>
 void
-MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Address boundsCheckLimit, L label)
 {
-    Instruction* inst = (Instruction*) patchAt;
-    InstImm* i0 = (InstImm*) inst;
-    InstImm* i1 = (InstImm*) i0->next();
-
-    // Replace with new value
-    AssemblerMIPSShared::UpdateLuiOriValue(i0, i1, limit);
+    MOZ_CRASH("NYI - patching is no longer available");
 }
 
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerMIPSCompat::incrementInt32Value(const Address& addr)
 {
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -702,33 +702,30 @@ MacroAssembler::storeUncanonicalizedFloa
     ma_ss(src, addr);
 }
 
 // ========================================================================
 // wasm support
 
 template <class L>
 void
-MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Register boundsCheckLimit, L label)
 {
-    BufferOffset bo = ma_BoundsCheck(ScratchRegister);
-    append(wasm::BoundsCheck(bo.getOffset()));
+    MOZ_CRASH("NYI - patching is no longer available");
+    // BufferOffset bo = ma_BoundsCheck(ScratchRegister);
+    // append(wasm::BoundsCheck(bo.getOffset()));
 
-    ma_b(index, ScratchRegister, label, cond);
+    // ma_b(index, ScratchRegister, label, cond);
 }
 
+template <class L>
 void
-MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Address boundsCheckLimit, L label)
 {
-    Instruction* inst = (Instruction*) patchAt;
-    InstImm* i0 = (InstImm*) inst;
-    InstImm* i1 = (InstImm*) i0->next();
-
-    // Replace with new value
-    AssemblerMIPSShared::UpdateLuiOriValue(i0, i1, limit);
+    MOZ_CRASH("NYI - patching is no longer available");
 }
 
 //}}} check_macroassembler_style
 // ===============================================================
 
 // The specializations for cmpPtrSet are outside the braces because check_macroassembler_style can't yet
 // deal with specializations.
 
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -190,16 +190,18 @@ class MacroAssemblerNone : public Assemb
     void trace(JSTracer*) { MOZ_CRASH(); }
     static void TraceJumpRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); }
     static void TraceDataRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); }
 
     static bool SupportsFloatingPoint() { return false; }
     static bool SupportsSimd() { return false; }
     static bool SupportsUnalignedAccesses() { return false; }
 
+    static bool HasRoundInstruction(RoundingMode) { return false; }
+
     void executableCopy(void*) { MOZ_CRASH(); }
     void copyJumpRelocationTable(uint8_t*) { MOZ_CRASH(); }
     void copyDataRelocationTable(uint8_t*) { MOZ_CRASH(); }
     void copyPreBarrierTable(uint8_t*) { MOZ_CRASH(); }
     void processCodeLabels(uint8_t*) { MOZ_CRASH(); }
 
     void flushBuffer() { MOZ_CRASH(); }
 
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -806,18 +806,16 @@ namespace jit {
 
 // The base class of all Assemblers for all archs.
 class AssemblerShared
 {
     wasm::CallSiteAndTargetVector callSites_;
     wasm::TrapSiteVector trapSites_;
     wasm::TrapFarJumpVector trapFarJumps_;
     wasm::MemoryAccessVector memoryAccesses_;
-    wasm::MemoryPatchVector memoryPatches_;
-    wasm::BoundsCheckVector boundsChecks_;
     wasm::SymbolicAccessVector symbolicAccesses_;
 
   protected:
     Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_;
 
     bool enoughMemory_;
     bool embedsNurseryPointers_;
 
@@ -882,22 +880,16 @@ class AssemblerShared
             // and thus requires a MemoryAccess.
             MOZ_ASSERT(access.isPlainAsmJS());
 #ifdef WASM_HUGE_MEMORY
             append(wasm::MemoryAccess(codeOffset));
 #endif
         }
     }
 
-    void append(wasm::MemoryPatch patch) { enoughMemory_ &= memoryPatches_.append(patch); }
-    wasm::MemoryPatchVector&& extractMemoryPatches() { return Move(memoryPatches_); }
-
-    void append(wasm::BoundsCheck check) { enoughMemory_ &= boundsChecks_.append(check); }
-    wasm::BoundsCheckVector&& extractBoundsChecks() { return Move(boundsChecks_); }
-
     void append(wasm::SymbolicAccess access) { enoughMemory_ &= symbolicAccesses_.append(access); }
     size_t numSymbolicAccesses() const { return symbolicAccesses_.length(); }
     wasm::SymbolicAccess symbolicAccess(size_t i) const { return symbolicAccesses_[i]; }
 
     static bool canUseInSingleByteInstruction(Register reg) { return true; }
 
     void addCodeLabel(CodeLabel label) {
         propagateOOM(codeLabels_.append(label));
@@ -924,26 +916,16 @@ class AssemblerShared
         for (; i < trapFarJumps_.length(); i++)
             trapFarJumps_[i].offsetBy(delta);
 
         i = memoryAccesses_.length();
         enoughMemory_ &= memoryAccesses_.appendAll(other.memoryAccesses_);
         for (; i < memoryAccesses_.length(); i++)
             memoryAccesses_[i].offsetBy(delta);
 
-        i = memoryPatches_.length();
-        enoughMemory_ &= memoryPatches_.appendAll(other.memoryPatches_);
-        for (; i < memoryPatches_.length(); i++)
-            memoryPatches_[i].offsetBy(delta);
-
-        i = boundsChecks_.length();
-        enoughMemory_ &= boundsChecks_.appendAll(other.boundsChecks_);
-        for (; i < boundsChecks_.length(); i++)
-            boundsChecks_[i].offsetBy(delta);
-
         i = symbolicAccesses_.length();
         enoughMemory_ &= symbolicAccesses_.appendAll(other.symbolicAccesses_);
         for (; i < symbolicAccesses_.length(); i++)
             symbolicAccesses_[i].patchAt.offsetBy(delta);
 
         i = codeLabels_.length();
         enoughMemory_ &= codeLabels_.appendAll(other.codeLabels_);
         for (; i < codeLabels_.length(); i++)
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -6949,61 +6949,66 @@ class LStringLength : public LInstructio
         setOperand(0, string);
     }
 
     const LAllocation* string() {
         return getOperand(0);
     }
 };
 
-// Take the floor of a double precision number. Implements Math.floor().
+// Take the floor of a double precision number and converts it to an int32.
+// Implements Math.floor().
 class LFloor : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(Floor)
 
     explicit LFloor(const LAllocation& num) {
         setOperand(0, num);
     }
 };
 
-// Take the floor of a single precision number. Implements Math.floor().
+// Take the floor of a single precision number and converts it to an int32.
+// Implements Math.floor().
 class LFloorF : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(FloorF)
 
     explicit LFloorF(const LAllocation& num) {
         setOperand(0, num);
     }
 };
 
-// Take the ceiling of a double precision number. Implements Math.ceil().
+// Take the ceiling of a double precision number and converts it to an int32.
+// Implements Math.ceil().
 class LCeil : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(Ceil)
 
     explicit LCeil(const LAllocation& num) {
         setOperand(0, num);
     }
 };
 
-// Take the ceiling of a single precision number. Implements Math.ceil().
+// Take the ceiling of a single precision number and converts it to an int32.
+// Implements Math.ceil().
 class LCeilF : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(CeilF)
 
     explicit LCeilF(const LAllocation& num) {
         setOperand(0, num);
     }
 };
 
-// Round a double precision number. Implements Math.round().
+// Round a double precision number and converts it to an int32.
+// Implements Math.round().
 class LRound : public LInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(Round)
 
     LRound(const LAllocation& num, const LDefinition& temp) {
         setOperand(0, num);
         setTemp(0, temp);
@@ -7012,17 +7017,18 @@ class LRound : public LInstructionHelper
     const LDefinition* temp() {
         return getTemp(0);
     }
     MRound* mir() const {
         return mir_->toRound();
     }
 };
 
-// Round a single precision number. Implements Math.round().
+// Round a single precision number and converts it to an int32.
+// Implements Math.round().
 class LRoundF : public LInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(RoundF)
 
     LRoundF(const LAllocation& num, const LDefinition& temp) {
         setOperand(0, num);
         setTemp(0, temp);
@@ -7031,16 +7037,46 @@ class LRoundF : public LInstructionHelpe
     const LDefinition* temp() {
         return getTemp(0);
     }
     MRound* mir() const {
         return mir_->toRound();
     }
 };
 
+// Rounds a double precision number accordingly to mir()->roundingMode(),
+// and keeps a double output.
+class LNearbyInt : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(NearbyInt)
+
+    explicit LNearbyInt(const LAllocation& num) {
+        setOperand(0, num);
+    }
+    MNearbyInt* mir() const {
+        return mir_->toNearbyInt();
+    }
+};
+
+// Rounds a single precision number accordingly to mir()->roundingMode(),
+// and keeps a single output.
+class LNearbyIntF : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(NearbyIntF)
+
+    explicit LNearbyIntF(const LAllocation& num) {
+        setOperand(0, num);
+    }
+    MNearbyInt* mir() const {
+        return mir_->toNearbyInt();
+    }
+};
+
 // Load a function's call environment.
 class LFunctionEnvironment : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(FunctionEnvironment)
 
     explicit LFunctionEnvironment(const LAllocation& function) {
         setOperand(0, function);
@@ -8020,207 +8056,274 @@ class LWasmAddOffset : public LInstructi
     MWasmAddOffset* mir() const {
         return mir_->toWasmAddOffset();
     }
     const LAllocation* base() {
         return getOperand(0);
     }
 };
 
-class LWasmBoundsCheck : public LInstructionHelper<0, 1, 0>
+class LWasmBoundsCheck : public LInstructionHelper<0, 2, 0>
 {
   public:
     LIR_HEADER(WasmBoundsCheck);
-    explicit LWasmBoundsCheck(const LAllocation& ptr) {
+    explicit LWasmBoundsCheck(const LAllocation& ptr,
+                              const LAllocation& boundsCheckLimit = LAllocation())
+    {
         setOperand(0, ptr);
+        setOperand(1, boundsCheckLimit);
     }
     MWasmBoundsCheck* mir() const {
         return mir_->toWasmBoundsCheck();
     }
     const LAllocation* ptr() {
         return getOperand(0);
     }
+    const LAllocation* boundsCheckLimit() {
+        return getOperand(1);
+    }
+};
+
+class LWasmLoadTls : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(WasmLoadTls);
+    explicit LWasmLoadTls(const LAllocation& tlsPtr) {
+        setOperand(0, tlsPtr);
+    }
+    MWasmLoadTls* mir() const {
+        return mir_->toWasmLoadTls();
+    }
+    const LAllocation* tlsPtr() {
+        return getOperand(0);
+    }
 };
 
 namespace details {
 
 // This is a base class for LWasmLoad/LWasmLoadI64.
 template<size_t Defs, size_t Temp>
-class LWasmLoadBase : public LInstructionHelper<Defs, 1, Temp>
-{
-  public:
-    typedef LInstructionHelper<Defs, 1, Temp> Base;
-    explicit LWasmLoadBase(const LAllocation& ptr) {
+class LWasmLoadBase : public LInstructionHelper<Defs, 2, Temp>
+{
+  public:
+    typedef LInstructionHelper<Defs, 2, Temp> Base;
+    explicit LWasmLoadBase(const LAllocation& ptr, const LAllocation& memoryBase) {
         Base::setOperand(0, ptr);
+        Base::setOperand(1, memoryBase);
     }
     MWasmLoad* mir() const {
         return Base::mir_->toWasmLoad();
     }
     const LAllocation* ptr() {
         return Base::getOperand(0);
     }
+    const LAllocation* memoryBase() {
+        return Base::getOperand(1);
+    }
 };
 
 } // namespace details
 
 class LWasmLoad : public details::LWasmLoadBase<1, 1>
 {
   public:
-    explicit LWasmLoad(const LAllocation& ptr)
-      : LWasmLoadBase(ptr)
+    explicit LWasmLoad(const LAllocation& ptr, const LAllocation& memoryBase = LAllocation())
+      : LWasmLoadBase(ptr, memoryBase)
     {
         setTemp(0, LDefinition::BogusTemp());
     }
 
     const LDefinition* ptrCopy() {
         return Base::getTemp(0);
     }
 
     LIR_HEADER(WasmLoad);
 };
 
 class LWasmLoadI64 : public details::LWasmLoadBase<INT64_PIECES, 1>
 {
   public:
-    explicit LWasmLoadI64(const LAllocation& ptr)
-      : LWasmLoadBase(ptr)
+    explicit LWasmLoadI64(const LAllocation& ptr, const LAllocation& memoryBase = LAllocation())
+      : LWasmLoadBase(ptr, memoryBase)
     {
         setTemp(0, LDefinition::BogusTemp());
     }
 
     const LDefinition* ptrCopy() {
         return Base::getTemp(0);
     }
 
     LIR_HEADER(WasmLoadI64);
 };
 
-class LWasmStore : public LInstructionHelper<0, 2, 1>
+class LWasmStore : public LInstructionHelper<0, 3, 1>
 {
   public:
     LIR_HEADER(WasmStore);
 
     static const size_t PtrIndex = 0;
     static const size_t ValueIndex = 1;
-
-    LWasmStore(const LAllocation& ptr, const LAllocation& value) {
+    static const size_t MemoryBaseIndex = 2;
+
+    LWasmStore(const LAllocation& ptr, const LAllocation& value,
+               const LAllocation& memoryBase = LAllocation())
+    {
         setOperand(PtrIndex, ptr);
         setOperand(ValueIndex, value);
+        setOperand(MemoryBaseIndex, memoryBase);
         setTemp(0, LDefinition::BogusTemp());
     }
     MWasmStore* mir() const {
         return mir_->toWasmStore();
     }
     const LAllocation* ptr() {
         return getOperand(PtrIndex);
     }
     const LDefinition* ptrCopy() {
         return getTemp(0);
     }
     const LAllocation* value() {
         return getOperand(ValueIndex);
     }
-};
-
-class LWasmStoreI64 : public LInstructionHelper<0, INT64_PIECES + 1, 1>
+    const LAllocation* memoryBase() {
+        return getOperand(MemoryBaseIndex);
+    }
+};
+
+class LWasmStoreI64 : public LInstructionHelper<0, INT64_PIECES + 2, 1>
 {
   public:
     LIR_HEADER(WasmStoreI64);
 
     static const size_t PtrIndex = 0;
-    static const size_t ValueIndex = 1;
-
-    LWasmStoreI64(const LAllocation& ptr, const LInt64Allocation& value) {
+    static const size_t MemoryBaseIndex = 1;
+    static const size_t ValueIndex = 2;
+
+    LWasmStoreI64(const LAllocation& ptr, const LInt64Allocation& value,
+                  const LAllocation& memoryBase = LAllocation())
+    {
         setOperand(PtrIndex, ptr);
+        setOperand(MemoryBaseIndex, memoryBase);
         setInt64Operand(ValueIndex, value);
         setTemp(0, LDefinition::BogusTemp());
     }
     MWasmStore* mir() const {
         return mir_->toWasmStore();
     }
     const LAllocation* ptr() {
         return getOperand(PtrIndex);
     }
+    const LAllocation* memoryBase() {
+        return getOperand(MemoryBaseIndex);
+    }
     const LDefinition* ptrCopy() {
         return getTemp(0);
     }
     const LInt64Allocation value() {
         return getInt64Operand(ValueIndex);
     }
 };
 
-class LAsmJSLoadHeap : public LInstructionHelper<1, 1, 0>
+class LAsmJSLoadHeap : public LInstructionHelper<1, 3, 0>
 {
   public:
     LIR_HEADER(AsmJSLoadHeap);
-    explicit LAsmJSLoadHeap(const LAllocation& ptr) {
+    explicit LAsmJSLoadHeap(const LAllocation& ptr, const LAllocation& boundsCheckLimit = LAllocation(),
+                            const LAllocation& memoryBase = LAllocation())
+    {
         setOperand(0, ptr);
+        setOperand(1, boundsCheckLimit);
+        setOperand(2, memoryBase);
     }
     MAsmJSLoadHeap* mir() const {
         return mir_->toAsmJSLoadHeap();
     }
     const LAllocation* ptr() {
         return getOperand(0);
     }
-};
-
-class LAsmJSStoreHeap : public LInstructionHelper<0, 2, 0>
+    const LAllocation* boundsCheckLimit() {
+        return getOperand(1);
+    }
+    const LAllocation* memoryBase() {
+        return getOperand(2);
+    }
+};
+
+class LAsmJSStoreHeap : public LInstructionHelper<0, 4, 0>
 {
   public:
     LIR_HEADER(AsmJSStoreHeap);
-    LAsmJSStoreHeap(const LAllocation& ptr, const LAllocation& value) {
+    LAsmJSStoreHeap(const LAllocation& ptr, const LAllocation& value,
+                    const LAllocation& boundsCheckLimit = LAllocation(),
+                    const LAllocation& memoryBase = LAllocation())
+    {
         setOperand(0, ptr);
         setOperand(1, value);
+        setOperand(2, boundsCheckLimit);
+        setOperand(3, memoryBase);
     }
     MAsmJSStoreHeap* mir() const {
         return mir_->toAsmJSStoreHeap();
     }
     const LAllocation* ptr() {
         return getOperand(0);
     }
     const LAllocation* value() {
         return getOperand(1);
     }
-};
-
-class LAsmJSCompareExchangeHeap : public LInstructionHelper<1, 3, 4>
+    const LAllocation* boundsCheckLimit() {
+        return getOperand(2);
+    }
+    const LAllocation* memoryBase() {
+        return getOperand(3);
+    }
+};
+
+class LAsmJSCompareExchangeHeap : public LInstructionHelper<1, 4, 4>
 {
   public:
     LIR_HEADER(AsmJSCompareExchangeHeap);
 
+    // ARM, ARM64, x86, x64
     LAsmJSCompareExchangeHeap(const LAllocation& ptr, const LAllocation& oldValue,
-                              const LAllocation& newValue)
+                              const LAllocation& newValue, const LAllocation& memoryBase = LAllocation())
     {
         setOperand(0, ptr);
         setOperand(1, oldValue);
         setOperand(2, newValue);
+        setOperand(3, memoryBase);
         setTemp(0, LDefinition::BogusTemp());
     }
+    // MIPS32, MIPS64
     LAsmJSCompareExchangeHeap(const LAllocation& ptr, const LAllocation& oldValue,
                               const LAllocation& newValue, const LDefinition& valueTemp,
                               const LDefinition& offsetTemp, const LDefinition& maskTemp)
     {
         setOperand(0, ptr);
         setOperand(1, oldValue);
         setOperand(2, newValue);
+        setOperand(3, LAllocation());
         setTemp(0, LDefinition::BogusTemp());
         setTemp(1, valueTemp);
         setTemp(2, offsetTemp);
         setTemp(3, maskTemp);
     }
 
     const LAllocation* ptr() {
         return getOperand(0);
     }
     const LAllocation* oldValue() {
         return getOperand(1);
     }
     const LAllocation* newValue() {
         return getOperand(2);
     }
+    const LAllocation* memoryBase() {
+        return getOperand(3);
+    }
     const LDefinition* addrTemp() {
         return getTemp(0);
     }
 
     void setAddrTemp(const LDefinition& addrTemp) {
         setTemp(0, addrTemp);
     }
 
@@ -8235,45 +8338,53 @@ class LAsmJSCompareExchangeHeap : public
         return getTemp(3);
     }
 
     MAsmJSCompareExchangeHeap* mir() const {
         return mir_->toAsmJSCompareExchangeHeap();
     }
 };
 
-class LAsmJSAtomicExchangeHeap : public LInstructionHelper<1, 2, 4>
+class LAsmJSAtomicExchangeHeap : public LInstructionHelper<1, 3, 4>
 {
   public:
     LIR_HEADER(AsmJSAtomicExchangeHeap);
 
-    LAsmJSAtomicExchangeHeap(const LAllocation& ptr, const LAllocation& value)
+    // ARM, ARM64, x86, x64
+    LAsmJSAtomicExchangeHeap(const LAllocation& ptr, const LAllocation& value,
+                             const LAllocation& memoryBase = LAllocation())
     {
         setOperand(0, ptr);
         setOperand(1, value);
+        setOperand(2, memoryBase);
         setTemp(0, LDefinition::BogusTemp());
     }
+    // MIPS32, MIPS64
     LAsmJSAtomicExchangeHeap(const LAllocation& ptr, const LAllocation& value,
                              const LDefinition& valueTemp, const LDefinition& offsetTemp,
                              const LDefinition& maskTemp)
     {
         setOperand(0, ptr);
         setOperand(1, value);
+        setOperand(2, LAllocation());
         setTemp(0, LDefinition::BogusTemp());
         setTemp(1, valueTemp);
         setTemp(2, offsetTemp);
         setTemp(3, maskTemp);
     }
 
     const LAllocation* ptr() {
         return getOperand(0);
     }
     const LAllocation* value() {
         return getOperand(1);
     }
+    const LAllocation* memoryBase() {
+        return getOperand(2);
+    }
     const LDefinition* addrTemp() {
         return getTemp(0);
     }
 
     void setAddrTemp(const LDefinition& addrTemp) {
         setTemp(0, addrTemp);
     }
 
@@ -8288,54 +8399,62 @@ class LAsmJSAtomicExchangeHeap : public 
         return getTemp(3);
     }
 
     MAsmJSAtomicExchangeHeap* mir() const {
         return mir_->toAsmJSAtomicExchangeHeap();
     }
 };
 
-class LAsmJSAtomicBinopHeap : public LInstructionHelper<1, 2, 6>
+class LAsmJSAtomicBinopHeap : public LInstructionHelper<1, 3, 6>
 {
   public:
     LIR_HEADER(AsmJSAtomicBinopHeap);
 
     static const int32_t valueOp = 1;
 
+    // ARM, ARM64, x86, x64
     LAsmJSAtomicBinopHeap(const LAllocation& ptr, const LAllocation& value,
                           const LDefinition& temp,
-                          const LDefinition& flagTemp = LDefinition::BogusTemp())
+                          const LDefinition& flagTemp = LDefinition::BogusTemp(),
+                          const LAllocation& memoryBase = LAllocation())
     {
         setOperand(0, ptr);
         setOperand(1, value);
+        setOperand(2, memoryBase);
         setTemp(0, temp);
         setTemp(1, LDefinition::BogusTemp());
         setTemp(2, flagTemp);
     }
+    // MIPS32, MIPS64
     LAsmJSAtomicBinopHeap(const LAllocation& ptr, const LAllocation& value,
                           const LDefinition& temp, const LDefinition& flagTemp,
                           const LDefinition& valueTemp, const LDefinition& offsetTemp,
                           const LDefinition& maskTemp)
     {
         setOperand(0, ptr);
         setOperand(1, value);
+        setOperand(2, LAllocation());
         setTemp(0, temp);
         setTemp(1, LDefinition::BogusTemp());
         setTemp(2, flagTemp);
         setTemp(3, valueTemp);
         setTemp(4, offsetTemp);
         setTemp(5, maskTemp);
     }
     const LAllocation* ptr() {
         return getOperand(0);
     }
     const LAllocation* value() {
         MOZ_ASSERT(valueOp == 1);
         return getOperand(1);
     }
+    const LAllocation* memoryBase() {
+        return getOperand(2);
+    }
     const LDefinition* temp() {
         return getTemp(0);
     }
 
     // Temp that may be used on some platforms to hold a computed address.
     const LDefinition* addrTemp() {
         return getTemp(1);
     }
@@ -8359,46 +8478,54 @@ class LAsmJSAtomicBinopHeap : public LIn
     }
 
     MAsmJSAtomicBinopHeap* mir() const {
         return mir_->toAsmJSAtomicBinopHeap();
     }
 };
 
 // Atomic binary operation where the result is discarded.
-class LAsmJSAtomicBinopHeapForEffect : public LInstructionHelper<0, 2, 5>
+class LAsmJSAtomicBinopHeapForEffect : public LInstructionHelper<0, 3, 5>
 {
   public:
     LIR_HEADER(AsmJSAtomicBinopHeapForEffect);
+    // ARM, ARM64, x86, x64
     LAsmJSAtomicBinopHeapForEffect(const LAllocation& ptr, const LAllocation& value,
-                                   const LDefinition& flagTemp = LDefinition::BogusTemp())
+                                   const LDefinition& flagTemp = LDefinition::BogusTemp(),
+                                   const LAllocation& memoryBase = LAllocation())
     {
         setOperand(0, ptr);
         setOperand(1, value);
+        setOperand(2, memoryBase);
         setTemp(0, LDefinition::BogusTemp());
         setTemp(1, flagTemp);
     }
+    // MIPS32, MIPS64
     LAsmJSAtomicBinopHeapForEffect(const LAllocation& ptr, const LAllocation& value,
                                    const LDefinition& flagTemp, const LDefinition& valueTemp,
                                    const LDefinition& offsetTemp, const LDefinition& maskTemp)
     {
         setOperand(0, ptr);
         setOperand(1, value);
+        setOperand(2, LAllocation());
         setTemp(0, LDefinition::BogusTemp());
         setTemp(1, flagTemp);
         setTemp(2, valueTemp);
         setTemp(3, offsetTemp);
         setTemp(4, maskTemp);
     }
     const LAllocation* ptr() {
         return getOperand(0);
     }
     const LAllocation* value() {
         return getOperand(1);
     }
+    const LAllocation* memoryBase() {
+        return getOperand(2);
+    }
 
     // Temp that may be used on some platforms to hold a computed address.
     const LDefinition* addrTemp() {
         return getTemp(0);
     }
     void setAddrTemp(const LDefinition& addrTemp) {
         setTemp(0, addrTemp);
     }
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -366,16 +366,18 @@
     _(ToAsync)                      \
     _(ToIdV)                        \
     _(Floor)                        \
     _(FloorF)                       \
     _(Ceil)                         \
     _(CeilF)                        \
     _(Round)                        \
     _(RoundF)                       \
+    _(NearbyInt)                    \
+    _(NearbyIntF)                   \
     _(In)                           \
     _(InArray)                      \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
     _(Rotate)                       \
     _(RotateI64)                    \
@@ -417,16 +419,17 @@
     _(WasmTruncateToInt32)          \
     _(WasmTrap)                     \
     _(WasmReinterpret)              \
     _(WasmReinterpretToI64)         \
     _(WasmReinterpretFromI64)       \
     _(WasmSelect)                   \
     _(WasmSelectI64)                \
     _(WasmBoundsCheck)              \
+    _(WasmLoadTls)                  \
     _(WasmAddOffset)                \
     _(WasmLoad)                     \
     _(WasmLoadI64)                  \
     _(WasmStore)                    \
     _(WasmStoreI64)                 \
     _(WasmLoadGlobalVar)            \
     _(WasmLoadGlobalVarI64)         \
     _(WasmStoreGlobalVar)           \
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -482,16 +482,24 @@ LAllocation
 LIRGeneratorShared::useRegisterOrConstantAtStart(MDefinition* mir)
 {
     if (mir->isConstant())
         return LAllocation(mir->toConstant());
     return useRegisterAtStart(mir);
 }
 
 LAllocation
+LIRGeneratorShared::useRegisterOrZero(MDefinition* mir)
+{
+    if (mir->isConstant() && mir->toConstant()->isInt32(0))
+        return LAllocation();
+    return useRegister(mir);
+}
+
+LAllocation
 LIRGeneratorShared::useRegisterOrZeroAtStart(MDefinition* mir)
 {
     if (mir->isConstant() && mir->toConstant()->isInt32(0))
         return LAllocation();
     return useRegisterAtStart(mir);
 }
 
 LAllocation
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -120,16 +120,17 @@ class LIRGeneratorShared : public MDefin
     // we can expect to write into memory in 1 instruction".
     inline LAllocation useStorable(MDefinition* mir);
     inline LAllocation useStorableAtStart(MDefinition* mir);
     inline LAllocation useKeepalive(MDefinition* mir);
     inline LAllocation useKeepaliveOrConstant(MDefinition* mir);
     inline LAllocation useRegisterOrConstant(MDefinition* mir);
     inline LAllocation useRegisterOrConstantAtStart(MDefinition* mir);
     inline LAllocation useRegisterOrZeroAtStart(MDefinition* mir);
+    inline LAllocation useRegisterOrZero(MDefinition* mir);
     inline LAllocation useRegisterOrNonDoubleConstant(MDefinition* mir);
 
     inline LUse useRegisterForTypedLoad(MDefinition* mir, MIRType type);
 
 #ifdef JS_NUNBOX32
     inline LUse useType(MDefinition* mir, LUse::Policy policy);
     inline LUse usePayload(MDefinition* mir, LUse::Policy policy);
     inline LUse usePayloadAtStart(MDefinition* mir, LUse::Policy policy);
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -191,24 +191,25 @@ LIRGeneratorX64::visitWasmUnsignedToFloa
     MOZ_ASSERT(ins->input()->type() == MIRType::Int32);
     LWasmUint32ToFloat32* lir = new(alloc()) LWasmUint32ToFloat32(useRegisterAtStart(ins->input()));
     define(lir, ins);
 }
 
 void
 LIRGeneratorX64::visitWasmLoad(MWasmLoad* ins)
 {
+    MDefinition* base = ins->base();
+    MOZ_ASSERT(base->type() == MIRType::Int32);
+
     if (ins->type() != MIRType::Int64) {
-        lowerWasmLoad(ins);
+        auto* lir = new(alloc()) LWasmLoad(useRegisterOrZeroAtStart(base));
+        define(lir, ins);
         return;
     }
 
-    MDefinition* base = ins->base();
-    MOZ_ASSERT(base->type() == MIRType::Int32);
-
     auto* lir = new(alloc()) LWasmLoadI64(useRegisterOrZeroAtStart(base));
     defineInt64(lir, ins);
 }
 
 void
 LIRGeneratorX64::visitWasmStore(MWasmStore* ins)
 {
     MDefinition* base = ins->base();
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -826,32 +826,16 @@ MacroAssembler::truncateDoubleToUInt64(A
 
     loadPtr(dest, temp);
     or64(Imm64(0x8000000000000000), Register64(temp));
     storePtr(temp, dest);
 
     bind(&done);
 }
 
-// ========================================================================
-// wasm support
-
-template <class L>
-void
-MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
-{
-    MOZ_CRASH("x64 should never emit a bounds check");
-}
-
-void
-MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
-{
-    MOZ_CRASH("x64 should never emit a bounds check");
-}
-
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerX64::incrementInt32Value(const Address& addr)
 {
     asMasm().addPtr(Imm32(1), addr);
 }
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -1119,16 +1119,27 @@ class AssemblerX86Shared : public Assemb
     static bool HasSSSE3() { return CPUInfo::IsSSSE3Present(); }
     static bool HasSSE41() { return CPUInfo::IsSSE41Present(); }
     static bool HasPOPCNT() { return CPUInfo::IsPOPCNTPresent(); }
     static bool SupportsFloatingPoint() { return CPUInfo::IsSSE2Present(); }
     static bool SupportsUnalignedAccesses() { return true; }
     static bool SupportsSimd() { return CPUInfo::IsSSE2Present(); }
     static bool HasAVX() { return CPUInfo::IsAVXPresent(); }
 
+    static bool HasRoundInstruction(RoundingMode mode) {
+        switch (mode) {
+          case RoundingMode::Up:
+          case RoundingMode::Down:
+          case RoundingMode::NearestTiesToEven:
+          case RoundingMode::TowardsZero:
+            return CPUInfo::IsSSE41Present();
+        }
+        MOZ_CRASH("unexpected mode");
+    }
+
     void cmpl(Register rhs, Register lhs) {
         masm.cmpl_rr(rhs.encoding(), lhs.encoding());
     }
     void cmpl(const Operand& rhs, Register lhs) {
         switch (rhs.kind()) {
           case Operand::REG:
             masm.cmpl_rr(rhs.reg(), lhs.encoding());
             break;
@@ -3353,24 +3364,40 @@ class AssemblerX86Shared : public Assemb
     void vsqrtsd(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         masm.vsqrtsd_rr(src1.encoding(), src0.encoding(), dest.encoding());
     }
     void vsqrtss(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         masm.vsqrtss_rr(src1.encoding(), src0.encoding(), dest.encoding());
     }
+
+    static X86Encoding::RoundingMode
+    ToX86RoundingMode(RoundingMode mode) {
+        switch (mode) {
+          case RoundingMode::Up:
+            return X86Encoding::RoundUp;
+          case RoundingMode::Down:
+            return X86Encoding::RoundDown;
+          case RoundingMode::NearestTiesToEven:
+            return X86Encoding::RoundToNearest;
+          case RoundingMode::TowardsZero:
+            return X86Encoding::RoundToZero;
+        }
+        MOZ_CRASH("unexpected mode");
+    }
     void vroundsd(X86Encoding::RoundingMode mode, FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE41());
         masm.vroundsd_irr(mode, src1.encoding(), src0.encoding(), dest.encoding());
     }
     void vroundss(X86Encoding::RoundingMode mode, FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE41());
         masm.vroundss_irr(mode, src1.encoding(), src0.encoding(), dest.encoding());
     }
+
     unsigned vinsertpsMask(unsigned sourceLane, unsigned destLane, unsigned zeroMask = 0)
     {
         // Note that the sourceLane bits are ignored in the case of a source
         // memory operand, and the source is the given 32-bits memory location.
         MOZ_ASSERT(zeroMask < 16);
         unsigned ret = zeroMask ;
         ret |= destLane << 4;
         ret |= sourceLane << 6;
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -2333,16 +2333,36 @@ CodeGeneratorX86Shared::visitRoundF(LRou
             // Cannot overflow: output was already checked against INT_MIN.
         }
     }
 
     masm.bind(&end);
 }
 
 void
+CodeGeneratorX86Shared::visitNearbyInt(LNearbyInt* lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+    FloatRegister output = ToFloatRegister(lir->output());
+
+    RoundingMode roundingMode = lir->mir()->roundingMode();
+    masm.vroundsd(Assembler::ToX86RoundingMode(roundingMode), input, output, output);
+}
+
+void
+CodeGeneratorX86Shared::visitNearbyIntF(LNearbyIntF* lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+    FloatRegister output = ToFloatRegister(lir->output());
+
+    RoundingMode roundingMode = lir->mir()->roundingMode();
+    masm.vroundss(Assembler::ToX86RoundingMode(roundingMode), input, output, output);
+}
+
+void
 CodeGeneratorX86Shared::visitGuardShape(LGuardShape* guard)
 {
     Register obj = ToRegister(guard->input());
     masm.cmpPtr(Operand(obj, ShapedObject::offsetOfShape()), ImmGCPtr(guard->mir()->shape()));
 
     bailoutIf(Assembler::NotEqual, guard->snapshot());
 }
 
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -231,16 +231,18 @@ class CodeGeneratorX86Shared : public Co
     virtual void visitMathD(LMathD* math);
     virtual void visitMathF(LMathF* math);
     virtual void visitFloor(LFloor* lir);
     virtual void visitFloorF(LFloorF* lir);
     virtual void visitCeil(LCeil* lir);
     virtual void visitCeilF(LCeilF* lir);
     virtual void visitRound(LRound* lir);
     virtual void visitRoundF(LRoundF* lir);
+    virtual void visitNearbyInt(LNearbyInt* lir);
+    virtual void visitNearbyIntF(LNearbyIntF* lir);
     virtual void visitGuardShape(LGuardShape* guard);
     virtual void visitGuardObjectGroup(LGuardObjectGroup* guard);
     virtual void visitGuardClass(LGuardClass* guard);
     virtual void visitEffectiveAddress(LEffectiveAddress* ins);
     virtual void visitUDivOrMod(LUDivOrMod* ins);
     virtual void visitUDivOrModConstant(LUDivOrModConstant *ins);
     virtual void visitWasmStackArg(LWasmStackArg* ins);
     virtual void visitWasmStackArgI64(LWasmStackArgI64* ins);
--- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp
@@ -320,28 +320,16 @@ LIRGeneratorX86Shared::visitAsmJSNeg(MAs
         defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins, 0);
         break;
       default:
         MOZ_CRASH();
     }
 }
 
 void
-LIRGeneratorX86Shared::lowerWasmLoad(MWasmLoad* ins)
-{
-    MOZ_ASSERT(ins->type() != MIRType::Int64);
-
-    MDefinition* base = ins->base();
-    MOZ_ASSERT(base->type() == MIRType::Int32);
-
-    auto* lir = new(alloc()) LWasmLoad(useRegisterOrZeroAtStart(base));
-    define(lir, ins);
-}
-
-void
 LIRGeneratorX86Shared::lowerUDiv(MDiv* div)
 {
     if (div->rhs()->isConstant()) {
         uint32_t rhs = div->rhs()->toConstant()->toInt32();
         int32_t shift = FloorLog2(rhs);
 
         LAllocation lhs = useRegisterAtStart(div->lhs());
         if (rhs != 0 && uint32_t(1) << shift == rhs) {
--- a/js/src/jit/x86-shared/Lowering-x86-shared.h
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.h
@@ -41,17 +41,16 @@ class LIRGeneratorX86Shared : public LIR
                      MDefinition* rhs);
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs);
     void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs);
     void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
                                  MDefinition* lhs, MDefinition* rhs);
     void visitAsmJSNeg(MAsmJSNeg* ins);
-    void lowerWasmLoad(MWasmLoad* ins);
     void visitWasmSelect(MWasmSelect* ins);
     void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
     void lowerDivI(MDiv* div);
     void lowerModI(MMod* mod);
     void lowerUDiv(MDiv* div);
     void lowerUMod(MMod* mod);
     void lowerUrshD(MUrsh* mir);
     void lowerTruncateDToInt32(MTruncateToInt32* ins);
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -611,16 +611,32 @@ class Assembler : public AssemblerX86Sha
           case Operand::MEM_ADDRESS32:
             masm.vmovss_mr(src.address(), dest.encoding());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
         return CodeOffset(masm.currentOffset());
     }
+    void vmovss(const Operand& src, FloatRegister dest) {
+        MOZ_ASSERT(HasSSE2());
+        switch (src.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.vmovss_mr_disp32(src.disp(), src.base(), dest.encoding());
+            break;
+          case Operand::MEM_ADDRESS32:
+            masm.vmovss_mr(src.address(), dest.encoding());
+            break;
+          case Operand::MEM_SCALE:
+            masm.vmovss_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
     CodeOffset vmovdWithPatch(const Operand& src, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         switch (src.kind()) {
           case Operand::MEM_REG_DISP:
             masm.vmovd_mr_disp32(src.disp(), src.base(), dest.encoding());
             break;
           case Operand::MEM_ADDRESS32:
             masm.vmovd_mr(src.address(), dest.encoding());
@@ -653,16 +669,32 @@ class Assembler : public AssemblerX86Sha
           case Operand::MEM_ADDRESS32:
             masm.vmovsd_mr(src.address(), dest.encoding());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
         return CodeOffset(masm.currentOffset());
     }
+    void vmovsd(const Operand& src, FloatRegister dest) {
+        MOZ_ASSERT(HasSSE2());
+        switch (src.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.vmovsd_mr_disp32(src.disp(), src.base(), dest.encoding());
+            break;
+          case Operand::MEM_ADDRESS32:
+            masm.vmovsd_mr(src.address(), dest.encoding());
+            break;
+          case Operand::MEM_SCALE:
+            masm.vmovsd_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
     CodeOffset vmovupsWithPatch(const Operand& src, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         switch (src.kind()) {
           case Operand::MEM_REG_DISP:
             masm.vmovups_mr_disp32(src.disp(), src.base(), dest.encoding());
             break;
           case Operand::MEM_ADDRESS32:
             masm.vmovups_mr(src.address(), dest.encoding());
@@ -794,30 +826,62 @@ class Assembler : public AssemblerX86Sha
           case Operand::MEM_ADDRESS32:
             masm.vmovss_rm(src.encoding(), dest.address());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
         return CodeOffset(masm.currentOffset());
     }
+    void vmovss(FloatRegister src, const Operand& dest) {
+        MOZ_ASSERT(HasSSE2());
+        switch (dest.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.vmovss_rm_disp32(src.encoding(), dest.disp(), dest.base());
+            break;
+          case Operand::MEM_ADDRESS32:
+            masm.vmovss_rm(src.encoding(), dest.address());
+            break;
+          case Operand::MEM_SCALE:
+            masm.vmovss_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
     CodeOffset vmovsdWithPatch(FloatRegister src, const Operand& dest) {
         MOZ_ASSERT(HasSSE2());
         switch (dest.kind()) {
           case Operand::MEM_REG_DISP:
             masm.vmovsd_rm_disp32(src.encoding(), dest.disp(), dest.base());
             break;
           case Operand::MEM_ADDRESS32:
             masm.vmovsd_rm(src.encoding(), dest.address());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
         return CodeOffset(masm.currentOffset());
     }
+    void vmovsd(FloatRegister src, const Operand& dest) {
+        MOZ_ASSERT(HasSSE2());
+        switch (dest.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.vmovsd_rm_disp32(src.encoding(), dest.disp(), dest.base());
+            break;
+          case Operand::MEM_ADDRESS32:
+            masm.vmovsd_rm(src.encoding(), dest.address());
+            break;
+          case Operand::MEM_SCALE:
+            masm.vmovsd_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
     CodeOffset vmovupsWithPatch(FloatRegister src, const Operand& dest) {
         MOZ_ASSERT(HasSSE2());
         switch (dest.kind()) {
           case Operand::MEM_REG_DISP:
             masm.vmovups_rm_disp32(src.encoding(), dest.disp(), dest.base());
             break;
           case Operand::MEM_ADDRESS32:
             masm.vmovups_rm(src.encoding(), dest.address());
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -347,20 +347,24 @@ void
 CodeGeneratorX86::emitWasmLoad(T* ins)
 {
     const MWasmLoad* mir = ins->mir();
 
     uint32_t offset = mir->access().offset();
     MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     const LAllocation* ptr = ins->ptr();
+    const LAllocation* memoryBase = ins->memoryBase();
+
+    // Lowering has set things up so that we can use a BaseIndex form if the
+    // pointer is constant and the offset is zero, or if the pointer is zero.
 
     Operand srcAddr = ptr->isBogus()
-                      ? Operand(PatchedAbsoluteAddress(offset))
-                      : Operand(ToRegister(ptr), offset);
+                      ? Operand(ToRegister(memoryBase), offset ? offset : mir->base()->toConstant()->toInt32())
+                      : Operand(ToRegister(memoryBase), ToRegister(ptr), TimesOne, offset);
 
     if (mir->type() == MIRType::Int64)
         masm.wasmLoadI64(mir->access(), srcAddr, ToOutRegister64(ins));
     else
         masm.wasmLoad(mir->access(), srcAddr, ToAnyRegister(ins->output()));
 }
 
 void
@@ -380,19 +384,24 @@ void
 CodeGeneratorX86::emitWasmStore(T* ins)
 {
     const MWasmStore* mir = ins->mir();
 
     uint32_t offset = mir->access().offset();
     MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     const LAllocation* ptr = ins->ptr();
+    const LAllocation* memoryBase = ins->memoryBase();
+
+    // Lowering has set things up so that we can use a BaseIndex form if the
+    // pointer is constant and the offset is zero, or if the pointer is zero.
+
     Operand dstAddr = ptr->isBogus()
-                      ? Operand(PatchedAbsoluteAddress(offset))
-                      : Operand(ToRegister(ptr), offset);
+                      ? Operand(ToRegister(memoryBase), offset ? offset : mir->base()->toConstant()->toInt32())
+                      : Operand(ToRegister(memoryBase), ToRegister(ptr), TimesOne, offset);
 
     if (mir->access().type() == Scalar::Int64) {
         Register64 value = ToRegister64(ins->getInt64Operand(LWasmStoreI64::ValueIndex));
         masm.wasmStoreI64(mir->access(), value, dstAddr);
     } else {
         AnyRegister value = ToAnyRegister(ins->getOperand(LWasmStore::ValueIndex));
         masm.wasmStore(mir->access(), value, dstAddr);
     }
@@ -412,32 +421,35 @@ CodeGeneratorX86::visitWasmStoreI64(LWas
 
 void
 CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
 {
     const MAsmJSLoadHeap* mir = ins->mir();
     MOZ_ASSERT(mir->access().offset() == 0);
 
     const LAllocation* ptr = ins->ptr();
+    const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
+    const LAllocation* memoryBase = ins->memoryBase();
     AnyRegister out = ToAnyRegister(ins->output());
 
     Scalar::Type accessType = mir->accessType();
     MOZ_ASSERT(!Scalar::isSimdType(accessType));
 
     OutOfLineLoadTypedArrayOutOfBounds* ool = nullptr;
     if (mir->needsBoundsCheck()) {
         ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(out, accessType);
         addOutOfLineCode(ool, mir);
 
-        masm.wasmBoundsCheck(Assembler::AboveOrEqual, ToRegister(ptr), ool->entry());
+        masm.wasmBoundsCheck(Assembler::AboveOrEqual, ToRegister(ptr), ToRegister(boundsCheckLimit),
+                             ool->entry());
     }
 
     Operand srcAddr = ptr->isBogus()
-                      ? Operand(PatchedAbsoluteAddress())
-                      : Operand(ToRegister(ptr), 0);
+                      ? Operand(ToRegister(memoryBase), 0)
+                      : Operand(ToRegister(memoryBase), ToRegister(ptr), TimesOne);
 
     masm.wasmLoad(mir->access(), srcAddr, out);
 
     if (ool)
         masm.bind(ool->rejoin());
 }
 
 void
@@ -492,62 +504,66 @@ CodeGeneratorX86::visitStoreTypedArrayEl
 void
 CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
 {
     const MAsmJSStoreHeap* mir = ins->mir();
     MOZ_ASSERT(mir->offset() == 0);
 
     const LAllocation* ptr = ins->ptr();
     const LAllocation* value = ins->value();
+    const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
+    const LAllocation* memoryBase = ins->memoryBase();
 
     Scalar::Type accessType = mir->accessType();
     MOZ_ASSERT(!Scalar::isSimdType(accessType));
     canonicalizeIfDeterministic(accessType, value);
 
     Operand dstAddr = ptr->isBogus()
-                      ? Operand(PatchedAbsoluteAddress())
-                      : Operand(ToRegister(ptr), 0);
+                      ? Operand(ToRegister(memoryBase), 0)
+                      : Operand(ToRegister(memoryBase), ToRegister(ptr), TimesOne);
 
     Label rejoin;
-    if (mir->needsBoundsCheck())
-        masm.wasmBoundsCheck(Assembler::AboveOrEqual, ToRegister(ptr), &rejoin);
+    if (mir->needsBoundsCheck()) {
+        masm.wasmBoundsCheck(Assembler::AboveOrEqual, ToRegister(ptr), ToRegister(boundsCheckLimit),
+                             &rejoin);
+    }
 
     masm.wasmStore(mir->access(), ToAnyRegister(value), dstAddr);
 
     if (rejoin.used())
         masm.bind(&rejoin);
 }
 
 // Perform bounds checking on the access if necessary; if it fails,
 // jump to out-of-line code that throws.  If the bounds check passes,
 // set up the heap address in addrTemp.
 
 void
-CodeGeneratorX86::asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg)
+CodeGeneratorX86::asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, Register memoryBase)
 {
     // Add in the actual heap pointer explicitly, to avoid opening up
     // the abstraction that is atomicBinopToTypedIntArray at this time.
     masm.movl(ptrReg, addrTemp);
-    masm.addlWithPatch(Imm32(0), addrTemp);
-    masm.append(wasm::MemoryPatch(masm.size()));
+    masm.addl(memoryBase, addrTemp);
 }
 
 void
 CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
 {
     MAsmJSCompareExchangeHeap* mir = ins->mir();
     MOZ_ASSERT(mir->access().offset() == 0);
 
     Scalar::Type accessType = mir->access().type();
     Register ptrReg = ToRegister(ins->ptr());
     Register oldval = ToRegister(ins->oldValue());
     Register newval = ToRegister(ins->newValue());
     Register addrTemp = ToRegister(ins->addrTemp());
+    Register memoryBase = ToRegister(ins->memoryBase());
 
-    asmJSAtomicComputeAddress(addrTemp, ptrReg);
+    asmJSAtomicComputeAddress(addrTemp, ptrReg, memoryBase);
 
     Address memAddr(addrTemp, 0);
     masm.compareExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
                                         memAddr,
                                         oldval,
                                         newval,
                                         InvalidReg,
                                         ToAnyRegister(ins->output()));
@@ -558,18 +574,19 @@ CodeGeneratorX86::visitAsmJSAtomicExchan
 {
     MAsmJSAtomicExchangeHeap* mir = ins->mir();
     MOZ_ASSERT(mir->access().offset() == 0);
 
     Scalar::Type accessType = mir->access().type();
     Register ptrReg = ToRegister(ins->ptr());
     Register value = ToRegister(ins->value());
     Register addrTemp = ToRegister(ins->addrTemp());
+    Register memoryBase = ToRegister(ins->memoryBase());
 
-    asmJSAtomicComputeAddress(addrTemp, ptrReg);
+    asmJSAtomicComputeAddress(addrTemp, ptrReg, memoryBase);
 
     Address memAddr(addrTemp, 0);
     masm.atomicExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
                                        memAddr,
                                        value,
                                        InvalidReg,
                                        ToAnyRegister(ins->output()));
 }
@@ -581,18 +598,19 @@ CodeGeneratorX86::visitAsmJSAtomicBinopH
     MOZ_ASSERT(mir->access().offset() == 0);
 
     Scalar::Type accessType = mir->access().type();
     Register ptrReg = ToRegister(ins->ptr());
     Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
     Register addrTemp = ToRegister(ins->addrTemp());
     const LAllocation* value = ins->value();
     AtomicOp op = mir->operation();
+    Register memoryBase = ToRegister(ins->memoryBase());
 
-    asmJSAtomicComputeAddress(addrTemp, ptrReg);
+    asmJSAtomicComputeAddress(addrTemp, ptrReg, memoryBase);
 
     Address memAddr(addrTemp, 0);
     if (value->isConstant()) {
         atomicBinopToTypedIntArray(op, accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
                                    Imm32(ToInt32(value)),
                                    memAddr,
                                    temp,
                                    InvalidReg,
@@ -614,18 +632,19 @@ CodeGeneratorX86::visitAsmJSAtomicBinopH
     MOZ_ASSERT(mir->access().offset() == 0);
     MOZ_ASSERT(!mir->hasUses());
 
     Scalar::Type accessType = mir->access().type();
     Register ptrReg = ToRegister(ins->ptr());
     Register addrTemp = ToRegister(ins->addrTemp());
     const LAllocation* value = ins->value();
     AtomicOp op = mir->operation();
+    Register memoryBase = ToRegister(ins->memoryBase());
 
-    asmJSAtomicComputeAddress(addrTemp, ptrReg);
+    asmJSAtomicComputeAddress(addrTemp, ptrReg, memoryBase);
 
     Address memAddr(addrTemp, 0);
     if (value->isConstant())
         atomicBinopToTypedIntArray(op, accessType, Imm32(ToInt32(value)), memAddr);
     else
         atomicBinopToTypedIntArray(op, accessType, ToRegister(value), memAddr);
 }
 
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -78,17 +78,17 @@ class CodeGeneratorX86 : public CodeGene
     void visitClzI64(LClzI64* lir);
     void visitCtzI64(LCtzI64* lir);
     void visitNotI64(LNotI64* lir);
     void visitWasmTruncateToInt64(LWasmTruncateToInt64* lir);
     void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
     void visitTestI64AndBranch(LTestI64AndBranch* lir);
 
   private:
-    void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg);
+    void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, Register memoryBase);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x86_CodeGenerator_x86_h */
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -270,25 +270,41 @@ LIRGeneratorX86::visitWasmUnsignedToFloa
     MOZ_ASSERT(ins->input()->type() == MIRType::Int32);
     LWasmUint32ToFloat32* lir = new(alloc()) LWasmUint32ToFloat32(useRegisterAtStart(ins->input()), temp());
     define(lir, ins);
 }
 
 void
 LIRGeneratorX86::visitWasmLoad(MWasmLoad* ins)
 {
+    MDefinition* base = ins->base();
+    MOZ_ASSERT(base->type() == MIRType::Int32);
+
+    MDefinition* memoryBase = ins->memoryBase();
+    MOZ_ASSERT(memoryBase->type() == MIRType::Pointer);
+
+    // If the base is a constant, and it is zero or its offset is zero, then
+    // code generation will fold the values into the access.  Allocate the
+    // pointer to a register only if that can't happen.
+
+    LAllocation baseAlloc;
+    if (!base->isConstant() || !(base->toConstant()->isInt32(0) || ins->access().offset() == 0))
+        baseAlloc = ins->type() == MIRType::Int64 ? useRegister(base) : useRegisterAtStart(base);
+
     if (ins->type() != MIRType::Int64) {
-        lowerWasmLoad(ins);
+        auto* lir = new(alloc()) LWasmLoad(baseAlloc, useRegisterAtStart(memoryBase));
+        define(lir, ins);
         return;
     }
 
-    MDefinition* base = ins->base();
-    MOZ_ASSERT(base->type() == MIRType::Int32);
+    // "AtStart" register usage does not work for the 64-bit case because we
+    // clobber two registers for the result and may need two registers for a
+    // scaled address; we can't guarantee non-interference.
 
-    auto* lir = new(alloc()) LWasmLoadI64(useRegisterOrZeroAtStart(base));
+    auto* lir = new(alloc()) LWasmLoadI64(baseAlloc, useRegister(memoryBase));
 
     Scalar::Type accessType = ins->access().type();
     if (accessType == Scalar::Int8 || accessType == Scalar::Int16 || accessType == Scalar::Int32) {
         // We use cdq to sign-extend the result and cdq demands these registers.
         defineInt64Fixed(lir, ins, LInt64Allocation(LAllocation(AnyRegister(edx)),
                                                     LAllocation(AnyRegister(eax))));
         return;
     }
@@ -297,17 +313,26 @@ LIRGeneratorX86::visitWasmLoad(MWasmLoad
 }
 
 void
 LIRGeneratorX86::visitWasmStore(MWasmStore* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
-    LAllocation baseAlloc = useRegisterOrZeroAtStart(base);
+    // If the base is a constant, and it is zero or its offset is zero, then
+    // code generation will fold the values into the access.  Allocate the
+    // pointer to a register only if that can't happen.
+
+    LAllocation baseAlloc;
+    if (!base->isConstant() || !(base->toConstant()->isInt32(0) || ins->access().offset() == 0))
+        baseAlloc = useRegisterAtStart(base);
+
+    MDefinition* memoryBase = ins->memoryBase();
+    MOZ_ASSERT(memoryBase->type() == MIRType::Pointer);
 
     LAllocation valueAlloc;
     switch (ins->access().type()) {
       case Scalar::Int8: case Scalar::Uint8:
         // See comment for LIRGeneratorX86::useByteOpRegister.
         valueAlloc = useFixed(ins->value(), eax);
         break;
       case Scalar::Int16: case Scalar::Uint16:
@@ -318,72 +343,94 @@ LIRGeneratorX86::visitWasmStore(MWasmSto
       case Scalar::Int16x8:
       case Scalar::Int32x4:
         // For now, don't allow constant values. The immediate operand affects
         // instruction layout which affects patching.
         valueAlloc = useRegisterAtStart(ins->value());
         break;
       case Scalar::Int64: {
         LInt64Allocation valueAlloc = useInt64RegisterAtStart(ins->value());
-        auto* lir = new(alloc()) LWasmStoreI64(baseAlloc, valueAlloc);
+        auto* lir = new(alloc()) LWasmStoreI64(baseAlloc, valueAlloc,
+                                               useRegisterAtStart(memoryBase));
         add(lir, ins);
         return;
       }
       case Scalar::Uint8Clamped:
       case Scalar::MaxTypedArrayViewType:
         MOZ_CRASH("unexpected array type");
     }
 
-    auto* lir = new(alloc()) LWasmStore(baseAlloc, valueAlloc);
+    auto* lir = new(alloc()) LWasmStore(baseAlloc, valueAlloc, useRegisterAtStart(memoryBase));
     add(lir, ins);
 }
 
 void
 LIRGeneratorX86::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
+    MDefinition* boundsCheckLimit = ins->boundsCheckLimit();
+    MOZ_ASSERT_IF(ins->needsBoundsCheck(), boundsCheckLimit->type() == MIRType::Int32);
+
+    MDefinition* memoryBase = ins->memoryBase();
+    MOZ_ASSERT(memoryBase->type() == MIRType::Pointer);
+
     // For simplicity, require a register if we're going to emit a bounds-check
     // branch, so that we don't have special cases for constants.
     LAllocation baseAlloc = ins->needsBoundsCheck()
                             ? useRegisterAtStart(base)
                             : useRegisterOrZeroAtStart(base);
+    LAllocation limitAlloc = ins->needsBoundsCheck()
+                           ? useRegisterAtStart(boundsCheckLimit)
+                           : LAllocation();
 
-    define(new(alloc()) LAsmJSLoadHeap(baseAlloc), ins);
+    auto* lir = new(alloc()) LAsmJSLoadHeap(baseAlloc, limitAlloc, useRegisterAtStart(memoryBase));
+    define(lir, ins);
 }
 
 void
 LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
+    MDefinition* boundsCheckLimit = ins->boundsCheckLimit();
+    MOZ_ASSERT_IF(ins->needsBoundsCheck(), boundsCheckLimit->type() == MIRType::Int32);
+
+    MDefinition* memoryBase = ins->memoryBase();
+    MOZ_ASSERT(memoryBase->type() == MIRType::Pointer);
+
     // For simplicity, require a register if we're going to emit a bounds-check
     // branch, so that we don't have special cases for constants.
     LAllocation baseAlloc = ins->needsBoundsCheck()
                             ? useRegisterAtStart(base)
                             : useRegisterOrZeroAtStart(base);
+    LAllocation limitAlloc = ins->needsBoundsCheck()
+                           ? useRegisterAtStart(boundsCheckLimit)
+                           : LAllocation();
 
     LAsmJSStoreHeap* lir = nullptr;
     switch (ins->access().type()) {
       case Scalar::Int8: case Scalar::Uint8:
         // See comment for LIRGeneratorX86::useByteOpRegister.
-        lir = new(alloc()) LAsmJSStoreHeap(baseAlloc, useFixed(ins->value(), eax));
+        lir = new(alloc()) LAsmJSStoreHeap(baseAlloc, useFixed(ins->value(), eax),
+                                           limitAlloc, useRegisterAtStart(memoryBase));
         break;
       case Scalar::Int16: case Scalar::Uint16:
       case Scalar::Int32: case Scalar::Uint32:
       case Scalar::Float32: case Scalar::Float64:
       case Scalar::Float32x4:
       case Scalar::Int8x16:
       case Scalar::Int16x8:
       case Scalar::Int32x4:
         // For now, don't allow constant values. The immediate operand affects
         // instruction layout which affects patching.
-        lir = new (alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()));
+        lir = new (alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()),
+                                            limitAlloc, useRegisterAtStart(memoryBase));
         break;
       case Scalar::Int64:
         MOZ_CRASH("NYI");
       case Scalar::Uint8Clamped:
       case Scalar::MaxTypedArrayViewType:
         MOZ_CRASH("unexpected array type");
     }
     add(lir, ins);
@@ -416,16 +463,19 @@ LIRGeneratorX86::visitStoreTypedArrayEle
 void
 LIRGeneratorX86::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
 {
     MOZ_ASSERT(ins->access().type() < Scalar::Float32);
 
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
+    MDefinition* memoryBase = ins->memoryBase();
+    MOZ_ASSERT(memoryBase->type() == MIRType::Pointer);
+
     bool byteArray = byteSize(ins->access().type()) == 1;
 
     // Register allocation:
     //
     // The output may not be used, but eax will be clobbered regardless
     // so pin the output to eax.
     //
     // oldval must be in a register.
@@ -435,63 +485,70 @@ LIRGeneratorX86::visitAsmJSCompareExchan
     // be ebx, ecx, or edx (eax is taken).
     //
     // Bug #1077036 describes some optimization opportunities.
 
     const LAllocation oldval = useRegister(ins->oldValue());
     const LAllocation newval = byteArray ? useFixed(ins->newValue(), ebx) : useRegister(ins->newValue());
 
     LAsmJSCompareExchangeHeap* lir =
-        new(alloc()) LAsmJSCompareExchangeHeap(useRegister(base), oldval, newval);
+        new(alloc()) LAsmJSCompareExchangeHeap(useRegister(base), oldval, newval, useRegister(memoryBase));
 
     lir->setAddrTemp(temp());
     defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
 }
 
 void
 LIRGeneratorX86::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
 {
     MOZ_ASSERT(ins->base()->type() == MIRType::Int32);
 
     const LAllocation base = useRegister(ins->base());
     const LAllocation value = useRegister(ins->value());
 
+    MDefinition* memoryBase = ins->memoryBase();
+    MOZ_ASSERT(memoryBase->type() == MIRType::Pointer);
+
     LAsmJSAtomicExchangeHeap* lir =
-        new(alloc()) LAsmJSAtomicExchangeHeap(base, value);
+        new(alloc()) LAsmJSAtomicExchangeHeap(base, value, useRegister(memoryBase));
 
     lir->setAddrTemp(temp());
     if (byteSize(ins->access().type()) == 1)
         defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
     else
         define(lir, ins);
 }
 
 void
 LIRGeneratorX86::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
 {
     MOZ_ASSERT(ins->access().type() < Scalar::Float32);
 
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
+    MDefinition* memoryBase = ins->memoryBase();
+    MOZ_ASSERT(memoryBase->type() == MIRType::Pointer);
+
     bool byteArray = byteSize(ins->access().type()) == 1;
 
     // Case 1: the result of the operation is not used.
     //
     // We'll emit a single instruction: LOCK ADD, LOCK SUB, LOCK AND,
     // LOCK OR, or LOCK XOR.  These can all take an immediate.
 
     if (!ins->hasUses()) {
         LAllocation value;
         if (byteArray && !ins->value()->isConstant())
             value = useFixed(ins->value(), ebx);
         else
             value = useRegisterOrConstant(ins->value());
         LAsmJSAtomicBinopHeapForEffect* lir =
-            new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(base), value);
+            new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(base), value, LDefinition::BogusTemp(),
+                                                        useRegister(memoryBase));
         lir->setAddrTemp(temp());
         add(lir, ins);
         return;
     }
 
     // Case 2: the result of the operation is used.
     //
     // For ADD and SUB we'll use XADD:
@@ -536,17 +593,18 @@ LIRGeneratorX86::visitAsmJSAtomicBinopHe
         value = useRegisterOrConstant(ins->value());
         if (bitOp)
             tempDef = temp();
     } else {
         value = useRegisterAtStart(ins->value());
     }
 
     LAsmJSAtomicBinopHeap* lir =
-        new(alloc()) LAsmJSAtomicBinopHeap(useRegister(base), value, tempDef);
+        new(alloc()) LAsmJSAtomicBinopHeap(useRegister(base), value, tempDef, LDefinition::BogusTemp(),
+                                           useRegister(memoryBase));
 
     lir->setAddrTemp(temp());
     if (byteArray || bitOp)
         defineFixed(lir, ins, LAllocation(AnyRegister(eax)));
     else if (ins->value()->isConstant())
         define(lir, ins);
     else
         defineReuseInput(lir, ins, LAsmJSAtomicBinopHeap::valueOp);
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -981,28 +981,28 @@ MacroAssembler::truncateDoubleToUInt64(A
     bind(&done);
 }
 
 // ========================================================================
 // wasm support
 
 template <class L>
 void
-MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Register boundsCheckLimit, L label)
 {
-    CodeOffset off = cmp32WithPatch(index, Imm32(0));
-    append(wasm::BoundsCheck(off.offset()));
-
+    cmp32(index, boundsCheckLimit);
     j(cond, label);
 }
 
+template <class L>
 void
-MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Address boundsCheckLimit, L label)
 {
-    reinterpret_cast<uint32_t*>(patchAt)[-1] = limit;
+    cmp32(index, Operand(boundsCheckLimit));
+    j(cond, label);
 }
 
 //}}} check_macroassembler_style
 // ===============================================================
 
 // Note: this function clobbers the source register.
 void
 MacroAssemblerX86::convertUInt32ToDouble(Register src, FloatRegister dest)
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -564,300 +564,264 @@ template void
 MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
                                   const BaseIndex& dest, MIRType slotType);
 
 // wasm specific methods, used in both the wasm baseline compiler and ion.
 
 void
 MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, Operand srcAddr, AnyRegister out)
 {
+    MOZ_ASSERT(srcAddr.kind() == Operand::MEM_REG_DISP || srcAddr.kind() == Operand::MEM_SCALE);
+
     memoryBarrier(access.barrierBefore());
 
     size_t loadOffset = size();
     switch (access.type()) {
       case Scalar::Int8:
-        movsblWithPatch(srcAddr, out.gpr());
+        movsbl(srcAddr, out.gpr());
         break;
       case Scalar::Uint8:
-        movzblWithPatch(srcAddr, out.gpr());
+        movzbl(srcAddr, out.gpr());
         break;
       case Scalar::Int16:
-        movswlWithPatch(srcAddr, out.gpr());
+        movswl(srcAddr, out.gpr());
         break;
       case Scalar::Uint16:
-        movzwlWithPatch(srcAddr, out.gpr());
+        movzwl(srcAddr, out.gpr());
         break;
       case Scalar::Int32:
       case Scalar::Uint32:
-        movlWithPatch(srcAddr, out.gpr());
+        movl(srcAddr, out.gpr());
         break;
       case Scalar::Float32:
-        vmovssWithPatch(srcAddr, out.fpu());
+        vmovss(srcAddr, out.fpu());
         break;
       case Scalar::Float64:
-        vmovsdWithPatch(srcAddr, out.fpu());
+        vmovsd(srcAddr, out.fpu());
         break;
       case Scalar::Float32x4:
         switch (access.numSimdElems()) {
           // In memory-to-register mode, movss zeroes out the high lanes.
-          case 1: vmovssWithPatch(srcAddr, out.fpu()); break;
+          case 1: vmovss(srcAddr, out.fpu()); break;
           // See comment above, which also applies to movsd.
-          case 2: vmovsdWithPatch(srcAddr, out.fpu()); break;
-          case 4: vmovupsWithPatch(srcAddr, out.fpu()); break;
+          case 2: vmovsd(srcAddr, out.fpu()); break;
+          case 4: vmovups(srcAddr, out.fpu()); break;
           default: MOZ_CRASH("unexpected size for partial load");
         }
         break;
       case Scalar::Int32x4:
         switch (access.numSimdElems()) {
           // In memory-to-register mode, movd zeroes out the high lanes.
-          case 1: vmovdWithPatch(srcAddr, out.fpu()); break;
+          case 1: vmovd(srcAddr, out.fpu()); break;
           // See comment above, which also applies to movq.
-          case 2: vmovqWithPatch(srcAddr, out.fpu()); break;
-          case 4: vmovdquWithPatch(srcAddr, out.fpu()); break;
+          case 2: vmovq(srcAddr, out.fpu()); break;
+          case 4: vmovdqu(srcAddr, out.fpu()); break;
           default: MOZ_CRASH("unexpected size for partial load");
         }
         break;
       case Scalar::Int8x16:
         MOZ_ASSERT(access.numSimdElems() == 16, "unexpected partial load");
-        vmovdquWithPatch(srcAddr, out.fpu());
+        vmovdqu(srcAddr, out.fpu());
         break;
       case Scalar::Int16x8:
         MOZ_ASSERT(access.numSimdElems() == 8, "unexpected partial load");
-        vmovdquWithPatch(srcAddr, out.fpu());
+        vmovdqu(srcAddr, out.fpu());
         break;
       case Scalar::Int64:
       case Scalar::Uint8Clamped:
       case Scalar::MaxTypedArrayViewType:
         MOZ_CRASH("unexpected type");
     }
-    append(wasm::MemoryPatch(size()));
     append(access, loadOffset, framePushed());
 
     memoryBarrier(access.barrierAfter());
 }
 
 void
 MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, Operand srcAddr, Register64 out)
 {
     MOZ_ASSERT(!access.isAtomic());
     MOZ_ASSERT(!access.isSimd());
+    MOZ_ASSERT(srcAddr.kind() == Operand::MEM_REG_DISP || srcAddr.kind() == Operand::MEM_SCALE);
 
     size_t loadOffset = size();
     switch (access.type()) {
       case Scalar::Int8:
         MOZ_ASSERT(out == Register64(edx, eax));
-        movsblWithPatch(srcAddr, out.low);
-        append(wasm::MemoryPatch(size()));
+        movsbl(srcAddr, out.low);
         append(access, loadOffset, framePushed());
 
         cdq();
         break;
       case Scalar::Uint8:
-        movzblWithPatch(srcAddr, out.low);
-        append(wasm::MemoryPatch(size()));
+        movzbl(srcAddr, out.low);
         append(access, loadOffset, framePushed());
 
         xorl(out.high, out.high);
         break;
       case Scalar::Int16:
         MOZ_ASSERT(out == Register64(edx, eax));
-        movswlWithPatch(srcAddr, out.low);
-        append(wasm::MemoryPatch(size()));
+        movswl(srcAddr, out.low);
         append(access, loadOffset, framePushed());
 
         cdq();
         break;
       case Scalar::Uint16:
-        movzwlWithPatch(srcAddr, out.low);
-        append(wasm::MemoryPatch(size()));
+        movzwl(srcAddr, out.low);
         append(access, loadOffset, framePushed());
 
         xorl(out.high, out.high);
         break;
       case Scalar::Int32:
         MOZ_ASSERT(out == Register64(edx, eax));
-        movlWithPatch(srcAddr, out.low);
-        append(wasm::MemoryPatch(size()));
+        movl(srcAddr, out.low);
         append(access, loadOffset, framePushed());
 
         cdq();
         break;
       case Scalar::Uint32:
-        movlWithPatch(srcAddr, out.low);
-        append(wasm::MemoryPatch(size()));
+        movl(srcAddr, out.low);
         append(access, loadOffset, framePushed());
 
         xorl(out.high, out.high);
         break;
-      case Scalar::Int64:
-        if (srcAddr.kind() == Operand::MEM_ADDRESS32) {
-            Operand low(PatchedAbsoluteAddress(uint32_t(srcAddr.address()) + INT64LOW_OFFSET));
-            Operand high(PatchedAbsoluteAddress(uint32_t(srcAddr.address()) + INT64HIGH_OFFSET));
+      case Scalar::Int64: {
+        Operand low(eax);
+        Operand high(eax);
 
-            movlWithPatch(low, out.low);
-            append(wasm::MemoryPatch(size()));
-            append(access, loadOffset, framePushed());
+        if (srcAddr.kind() == Operand::MEM_SCALE) {
+            BaseIndex addr = srcAddr.toBaseIndex();
 
-            loadOffset = size();
-            movlWithPatch(high, out.high);
-            append(wasm::MemoryPatch(size()));
-            append(access, loadOffset, framePushed());
+            MOZ_RELEASE_ASSERT(addr.base != out.low && addr.index != out.low);
+
+            low = Operand(addr.base, addr.index, addr.scale, addr.offset + INT64LOW_OFFSET);
+            high = Operand(addr.base, addr.index, addr.scale, addr.offset + INT64HIGH_OFFSET);
         } else {
-            MOZ_ASSERT(srcAddr.kind() == Operand::MEM_REG_DISP);
             Address addr = srcAddr.toAddress();
-            Operand low(addr.base, addr.offset + INT64LOW_OFFSET);
-            Operand high(addr.base, addr.offset + INT64HIGH_OFFSET);
 
-            if (addr.base != out.low) {
-                movlWithPatch(low, out.low);
-                append(wasm::MemoryPatch(size()));
-                append(access, loadOffset, framePushed());
+            MOZ_RELEASE_ASSERT(addr.base != out.low);
+
+            low = Operand(addr.base, addr.offset + INT64LOW_OFFSET);
+            high = Operand(addr.base, addr.offset + INT64HIGH_OFFSET);
+        }
 
-                loadOffset = size();
-                movlWithPatch(high, out.high);
-                append(wasm::MemoryPatch(size()));
-                append(access, loadOffset, framePushed());
-            } else {
-                MOZ_ASSERT(addr.base != out.high);
-                movlWithPatch(high, out.high);
-                append(wasm::MemoryPatch(size()));
-                append(access, loadOffset, framePushed());
+        movl(low, out.low);
+        append(access, loadOffset, framePushed());
 
-                loadOffset = size();
-                movlWithPatch(low, out.low);
-                append(wasm::MemoryPatch(size()));
-                append(access, loadOffset, framePushed());
-            }
-        }
+        loadOffset = size();
+        movl(high, out.high);
+        append(access, loadOffset, framePushed());
+
         break;
+      }
       case Scalar::Float32:
       case Scalar::Float64:
       case Scalar::Float32x4:
       case Scalar::Int8x16:
       case Scalar::Int16x8:
       case Scalar::Int32x4:
         MOZ_CRASH("non-int64 loads should use load()");
       case Scalar::Uint8Clamped:
       case Scalar::MaxTypedArrayViewType:
         MOZ_CRASH("unexpected array type");
     }
 }
 
 void
 MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value, Operand dstAddr)
 {
+    MOZ_ASSERT(dstAddr.kind() == Operand::MEM_REG_DISP || dstAddr.kind() == Operand::MEM_SCALE);
+
     memoryBarrier(access.barrierBefore());
 
     size_t storeOffset = size();
     switch (access.type()) {
       case Scalar::Int8:
       case Scalar::Uint8Clamped:
       case Scalar::Uint8:
-        movbWithPatch(value.gpr(), dstAddr);
+        movb(value.gpr(), dstAddr);
         break;
       case Scalar::Int16:
       case Scalar::Uint16:
-        movwWithPatch(value.gpr(), dstAddr);
+        movw(value.gpr(), dstAddr);
         break;
       case Scalar::Int32:
       case Scalar::Uint32:
-        movlWithPatch(value.gpr(), dstAddr);
+        movl(value.gpr(), dstAddr);
         break;
       case Scalar::Float32:
-        vmovssWithPatch(value.fpu(), dstAddr);
+        vmovss(value.fpu(), dstAddr);
         break;
       case Scalar::Float64:
-        vmovsdWithPatch(value.fpu(), dstAddr);
+        vmovsd(value.fpu(), dstAddr);
         break;
       case Scalar::Float32x4:
         switch (access.numSimdElems()) {
           // In memory-to-register mode, movss zeroes out the high lanes.
-          case 1: vmovssWithPatch(value.fpu(), dstAddr); break;
+          case 1: vmovss(value.fpu(), dstAddr); break;
           // See comment above, which also applies to movsd.
-          case 2: vmovsdWithPatch(value.fpu(), dstAddr); break;
-          case 4: vmovupsWithPatch(value.fpu(), dstAddr); break;
+          case 2: vmovsd(value.fpu(), dstAddr); break;
+          case 4: vmovups(value.fpu(), dstAddr); break;
           default: MOZ_CRASH("unexpected size for partial load");
         }
         break;
       case Scalar::Int32x4:
         switch (access.numSimdElems()) {
           // In memory-to-register mode, movd zeroes out the high lanes.
-          case 1: vmovdWithPatch(value.fpu(), dstAddr); break;
+          case 1: vmovd(value.fpu(), dstAddr); break;
           // See comment above, which also applies to movsd.
-          case 2: vmovqWithPatch(value.fpu(), dstAddr); break;
-          case 4: vmovdquWithPatch(value.fpu(), dstAddr); break;
+          case 2: vmovq(value.fpu(), dstAddr); break;
+          case 4: vmovdqu(value.fpu(), dstAddr); break;
           default: MOZ_CRASH("unexpected size for partial load");
         }
         break;
       case Scalar::Int8x16:
         MOZ_ASSERT(access.numSimdElems() == 16, "unexpected partial store");
-        vmovdquWithPatch(value.fpu(), dstAddr);
+        vmovdqu(value.fpu(), dstAddr);
         break;
       case Scalar::Int16x8:
         MOZ_ASSERT(access.numSimdElems() == 8, "unexpected partial store");
-        vmovdquWithPatch(value.fpu(), dstAddr);
+        vmovdqu(value.fpu(), dstAddr);
         break;
       case Scalar::Int64:
         MOZ_CRASH("Should be handled in storeI64.");
       case Scalar::MaxTypedArrayViewType:
         MOZ_CRASH("unexpected type");
     }
-    append(wasm::MemoryPatch(size()));
     append(access, storeOffset, framePushed());
 
     memoryBarrier(access.barrierAfter());
 }
 
 void
 MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value, Operand dstAddr)
 {
     MOZ_ASSERT(!access.isAtomic());
     MOZ_ASSERT(!access.isSimd());
+    MOZ_ASSERT(dstAddr.kind() == Operand::MEM_REG_DISP || dstAddr.kind() == Operand::MEM_SCALE);
+
+    Operand low(eax);
+    Operand high(eax);
+    if (dstAddr.kind() == Operand::MEM_SCALE) {
+        BaseIndex addr = dstAddr.toBaseIndex();
+        low = Operand(addr.base, addr.index, addr.scale, addr.offset + INT64LOW_OFFSET);
+        high = Operand(addr.base, addr.index, addr.scale, addr.offset + INT64HIGH_OFFSET);
+    } else {
+        Address addr = dstAddr.toAddress();
+        low = Operand(addr.base, addr.offset + INT64LOW_OFFSET);
+        high = Operand(addr.base, addr.offset + INT64HIGH_OFFSET);
+    }
 
     size_t storeOffset = size();
-    if (dstAddr.kind() == Operand::MEM_ADDRESS32) {
-        Operand low(PatchedAbsoluteAddress(uint32_t(dstAddr.address()) + INT64LOW_OFFSET));
-        Operand high(PatchedAbsoluteAddress(uint32_t(dstAddr.address()) + INT64HIGH_OFFSET));
-
-        movlWithPatch(value.low, low);
-        append(wasm::MemoryPatch(size()));
-        append(access, storeOffset, framePushed());
-
-        storeOffset = size();
-        movlWithPatch(value.high, high);
-        append(wasm::MemoryPatch(size()));
-        append(access, storeOffset, framePushed());
-    } else {
-        MOZ_ASSERT(dstAddr.kind() == Operand::MEM_REG_DISP);
-        Address addr = dstAddr.toAddress();
-        Operand low(addr.base, addr.offset + INT64LOW_OFFSET);
-        Operand high(addr.base, addr.offset + INT64HIGH_OFFSET);
+    movl(value.low, low);
+    append(access, storeOffset, framePushed());
 
-        if (addr.base != value.low) {
-            movlWithPatch(value.low, low);
-            append(wasm::MemoryPatch(size()));
-            append(access, storeOffset, framePushed());
-
-            storeOffset = size();
-            movlWithPatch(value.high, high);
-            append(wasm::MemoryPatch(size()));
-            append(access, storeOffset, framePushed());
-        } else {
-            MOZ_ASSERT(addr.base != value.high);
-
-            movlWithPatch(value.high, high);
-            append(wasm::MemoryPatch(size()));
-            append(access, storeOffset, framePushed());
-
-            storeOffset = size();
-            movlWithPatch(value.low, low);
-            append(wasm::MemoryPatch(size()));
-            append(access, storeOffset, framePushed());
-        }
-    }
+    storeOffset = size();
+    movl(value.high, high);
+    append(access, storeOffset, framePushed());
 }
 
 void
 MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
 {
     Label done;
     vcvttsd2si(input, output);
     branch32(Assembler::Condition::NotSigned, output, Imm32(0), &done);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -75,16 +75,18 @@ MSG_DEF(JSMSG_BAD_SURROGATE_CHAR,      1
 MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE,     1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
 MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR,     1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
 MSG_DEF(JSMSG_BUILTIN_CTOR_NO_NEW,     1, JSEXN_TYPEERR, "calling a builtin {0} constructor without new is forbidden")
 MSG_DEF(JSMSG_BAD_GENERATOR_YIELD,     1, JSEXN_TYPEERR, "yield from closing generator {0}")
 MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE,      0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
 MSG_DEF(JSMSG_UNEXPECTED_TYPE,         2, JSEXN_TYPEERR, "{0} is {1}")
 MSG_DEF(JSMSG_MISSING_FUN_ARG,         2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
 MSG_DEF(JSMSG_NOT_NONNULL_OBJECT,      1, JSEXN_TYPEERR, "{0} is not a non-null object")
+MSG_DEF(JSMSG_NOT_NONNULL_OBJECT_NAME, 2, JSEXN_TYPEERR, "{0} must be an object, got {1}")
+MSG_DEF(JSMSG_NOT_NONNULL_OBJECT_ARG,  3, JSEXN_TYPEERR, "{0} argument of {1} must be an object, got {2}")
 MSG_DEF(JSMSG_SET_NON_OBJECT_RECEIVER, 1, JSEXN_TYPEERR, "can't assign to properties of {0}: not an object")
 MSG_DEF(JSMSG_INVALID_DESCRIPTOR,      0, JSEXN_TYPEERR, "property descriptors must not specify a value or be writable when a getter or setter has been specified")
 MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE,   1, JSEXN_TYPEERR, "{0}: Object is not extensible")
 MSG_DEF(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE, 2, JSEXN_TYPEERR, "can't define property {1}: {0} is not extensible")
 MSG_DEF(JSMSG_CANT_REDEFINE_PROP,      1, JSEXN_TYPEERR, "can't redefine non-configurable property {0}")
 MSG_DEF(JSMSG_CANT_REDEFINE_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't redefine array length")
 MSG_DEF(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't define array index property past the end of an array with non-writable length")
 MSG_DEF(JSMSG_BAD_GET_SET_FIELD,       1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
@@ -212,26 +214,28 @@ MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD,     1
 MSG_DEF(JSMSG_BAD_STRICT_ASSIGN,       1, JSEXN_SYNTAXERR, "'{0}' can't be defined or assigned to in strict mode code")
 MSG_DEF(JSMSG_BAD_SWITCH,              0, JSEXN_SYNTAXERR, "invalid switch statement")
 MSG_DEF(JSMSG_BAD_SUPER,               0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
 MSG_DEF(JSMSG_BAD_SUPERPROP,           1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
 MSG_DEF(JSMSG_BAD_SUPERCALL,           0, JSEXN_SYNTAXERR, "super() is only valid in derived class constructors")
 MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
 MSG_DEF(JSMSG_BRACKET_AFTER_LIST,      0, JSEXN_SYNTAXERR, "missing ] after element list")
 MSG_DEF(JSMSG_BRACKET_IN_INDEX,        0, JSEXN_SYNTAXERR, "missing ] in index expression")
+MSG_DEF(JSMSG_BRACKET_OPENED,          2, JSEXN_NOTE, "[ opened at line {0}, column {1}")
 MSG_DEF(JSMSG_CATCH_AFTER_GENERAL,     0, JSEXN_SYNTAXERR, "catch after unconditional catch")
 MSG_DEF(JSMSG_CATCH_IDENTIFIER,        0, JSEXN_SYNTAXERR, "missing identifier in catch")
 MSG_DEF(JSMSG_CATCH_OR_FINALLY,        0, JSEXN_SYNTAXERR, "missing catch or finally after try")
 MSG_DEF(JSMSG_CATCH_WITHOUT_TRY,       0, JSEXN_SYNTAXERR, "catch without try")
 MSG_DEF(JSMSG_COLON_AFTER_CASE,        0, JSEXN_SYNTAXERR, "missing : after case label")
 MSG_DEF(JSMSG_COLON_AFTER_ID,          0, JSEXN_SYNTAXERR, "missing : after property id")
 MSG_DEF(JSMSG_COLON_IN_COND,           0, JSEXN_SYNTAXERR, "missing : in conditional expression")
 MSG_DEF(JSMSG_COMP_PROP_UNTERM_EXPR,   0, JSEXN_SYNTAXERR, "missing ] in computed property name")
 MSG_DEF(JSMSG_CONTRARY_NONDIRECTIVE,   1, JSEXN_SYNTAXERR, "'{0}' statement won't be enforced as a directive because it isn't in directive prologue position")
 MSG_DEF(JSMSG_CURLY_AFTER_BODY,        0, JSEXN_SYNTAXERR, "missing } after function body")
+MSG_DEF(JSMSG_CURLY_OPENED,            2, JSEXN_NOTE, "{ opened at line {0}, column {1}")
 MSG_DEF(JSMSG_CURLY_AFTER_CATCH,       0, JSEXN_SYNTAXERR, "missing } after catch block")
 MSG_DEF(JSMSG_CURLY_AFTER_FINALLY,     0, JSEXN_SYNTAXERR, "missing } after finally block")
 MSG_DEF(JSMSG_CURLY_AFTER_LIST,        0, JSEXN_SYNTAXERR, "missing } after property list")
 MSG_DEF(JSMSG_CURLY_AFTER_TRY,         0, JSEXN_SYNTAXERR, "missing } after try block")
 MSG_DEF(JSMSG_CURLY_BEFORE_BODY,       0, JSEXN_SYNTAXERR, "missing { before function body")
 MSG_DEF(JSMSG_CURLY_BEFORE_CATCH,      0, JSEXN_SYNTAXERR, "missing { before catch block")
 MSG_DEF(JSMSG_CURLY_BEFORE_CLASS,      0, JSEXN_SYNTAXERR, "missing { before class body")
 MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY,    0, JSEXN_SYNTAXERR, "missing { before finally block")
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -740,16 +740,18 @@ struct JSCompartment
 
     // Whether the given name is in [[VarNames]].
     bool isInVarNames(JS::Handle<JSAtom*> name) {
         return varNames_.has(name);
     }
 
     void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
 
+    MOZ_MUST_USE bool findDeadProxyZoneEdges(bool* foundAny);
+
     js::DtoaCache dtoaCache;
     js::NewProxyCache newProxyCache;
 
     // Random number generator for Math.random().
     mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator;
 
     // Initialize randomNumberGenerator if needed.
     void ensureRandomNumberGenerator();
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -487,16 +487,26 @@ IterateGrayObjects(JS::Zone* zone, GCThi
 
 /**
  * Invoke cellCallback on every gray JSObject in the given zone while cycle
  * collection is in progress.
  */
 extern JS_FRIEND_API(void)
 IterateGrayObjectsUnderCC(JS::Zone* zone, GCThingCallback cellCallback, void* data);
 
+#ifdef DEBUG
+// Trace the heap and check there are no black to gray edges. These are
+// not allowed since the cycle collector could throw away the gray thing and
+// leave a dangling pointer.
+//
+// This doesn't trace weak maps as these are handled separately.
+extern JS_FRIEND_API(bool)
+CheckGrayMarkingState(JSContext* cx);
+#endif
+
 #ifdef JS_HAS_CTYPES
 extern JS_FRIEND_API(size_t)
 SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject* obj);
 #endif
 
 extern JS_FRIEND_API(JSCompartment*)
 GetAnyCompartmentInZone(JS::Zone* zone);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3177,17 +3177,17 @@ GCRuntime::sweepBackgroundThings(ZoneLis
                 if (arenas)
                     ArenaLists::backgroundFinalize(&fop, arenas, &emptyArenas);
             }
         }
     }
 
     AutoLockGC lock(rt);
 
-    // Release swept areans, dropping and reaquiring the lock every so often to
+    // Release swept arenas, dropping and reaquiring the lock every so often to
     // avoid blocking the active thread from allocating chunks.
     static const size_t LockReleasePeriod = 32;
     size_t releaseCount = 0;
     Arena* next;
     for (Arena* arena = emptyArenas; arena; arena = next) {
         next = arena->next;
         rt->gc.releaseArena(arena, lock);
         releaseCount++;
@@ -4395,18 +4395,18 @@ DropStringWrappers(JSRuntime* rt)
     }
 }
 
 /*
  * Group zones that must be swept at the same time.
  *
  * If compartment A has an edge to an unmarked object in compartment B, then we
  * must not sweep A in a later slice than we sweep B. That's because a write
- * barrier in A that could lead to the unmarked object in B becoming
- * marked. However, if we had already swept that object, we would be in trouble.
+ * barrier in A could lead to the unmarked object in B becoming marked.
+ * However, if we had already swept that object, we would be in trouble.
  *
  * If we consider these dependencies as a graph, then all the compartments in
  * any strongly-connected component of this graph must be swept in the same
  * slice.
  *
  * Tarjan's algorithm is used to calculate the components.
  */
 namespace {
@@ -4446,16 +4446,38 @@ JSCompartment::findOutgoingEdges(ZoneCom
         if (key.is<JSObject*>()) {
             TenuredCell& other = key.as<JSObject*>()->asTenured();
             needsEdge = !other.isMarked(BLACK) || other.isMarked(GRAY);
         }
         key.applyToWrapped(AddOutgoingEdgeFunctor(needsEdge, finder));
     }
 }
 
+bool
+JSCompartment::findDeadProxyZoneEdges(bool* foundAny)
+{
+    // As an optimization, return whether any dead proxy objects are found in
+    // this compartment so that if a zone has none, its cross compartment
+    // wrappers do not need to be scanned.
+    *foundAny = false;
+    for (js::WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
+        Value value = e.front().value().get();
+        if (value.isObject()) {
+            if (IsDeadProxyObject(&value.toObject())) {
+                *foundAny = true;
+                CrossCompartmentKey& key = e.front().mutableKey();
+                if (!key.as<JSObject*>()->zone()->gcZoneGroupEdges().put(zone()))
+                    return false;
+            }
+        }
+    }
+
+    return true;
+}
+
 void
 Zone::findOutgoingEdges(ZoneComponentFinder& finder)
 {
     /*
      * Any compartment may have a pointer to an atom in the atoms
      * compartment, and these aren't in the cross compartment map.
      */
     JSRuntime* rt = runtimeFromActiveCooperatingThread();
@@ -4470,47 +4492,61 @@ Zone::findOutgoingEdges(ZoneComponentFin
         if (r.front()->isGCMarking())
             finder.addEdgeTo(r.front());
     }
 
     Debugger::findZoneEdges(this, finder);
 }
 
 bool
-GCRuntime::findZoneEdgesForWeakMaps()
+GCRuntime::findInterZoneEdges()
 {
     /*
      * Weakmaps which have keys with delegates in a different zone introduce the
      * need for zone edges from the delegate's zone to the weakmap zone.
      *
      * Since the edges point into and not away from the zone the weakmap is in
      * we must find these edges in advance and store them in a set on the Zone.
      * If we run out of memory, we fall back to sweeping everything in one
      * group.
      */
 
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         if (!WeakMapBase::findInterZoneEdges(zone))
             return false;
     }
 
+    for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+        if (zone->hasDeadProxies()) {
+            bool foundInZone = false;
+            for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
+                bool foundInCompartment = false;
+                if (!comp->findDeadProxyZoneEdges(&foundInCompartment))
+                    return false;
+                foundInZone = foundInZone || foundInCompartment;
+            }
+            if (!foundInZone)
+                zone->setHasDeadProxies(false);
+        }
+    }
+
     return true;
 }
 
 void
 GCRuntime::findZoneGroups(AutoLockForExclusiveAccess& lock)
 {
 #ifdef DEBUG
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
         MOZ_ASSERT(zone->gcZoneGroupEdges().empty());
 #endif
 
     JSContext* cx = TlsContext.get();
     ZoneComponentFinder finder(cx->nativeStackLimit[JS::StackForSystemCode], lock);
-    if (!isIncremental || !findZoneEdgesForWeakMaps())
+    if (!isIncremental || !findInterZoneEdges())
         finder.useOneComponent();
 
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCMarking());
         finder.addNode(zone);
     }
     zoneGroups = finder.getResultsList();
     currentZoneGroup = zoneGroups;
@@ -4775,16 +4811,18 @@ ResetGrayList(JSCompartment* comp)
 void
 js::NotifyGCNukeWrapper(JSObject* obj)
 {
     /*
      * References to target of wrapper are being removed, we no longer have to
      * remember to mark it.
      */
     RemoveFromGrayList(obj);
+
+    obj->zone()->setHasDeadProxies(true);
 }
 
 enum {
     JS_GC_SWAP_OBJECT_A_REMOVED = 1 << 0,
     JS_GC_SWAP_OBJECT_B_REMOVED = 1 << 1
 };
 
 unsigned
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/TemplateLib.h"
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
+#include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jsnum.h"
 #include "jsopcode.h"
 #include "jsprf.h"
 #include "jsscript.h"
@@ -82,16 +83,40 @@ js::ReportNotObject(JSContext* cx, const
 
     RootedValue value(cx, v);
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr);
     if (bytes)
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
                                    bytes.get());
 }
 
+void
+js::ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun, HandleValue v)
+{
+    MOZ_ASSERT(!v.isObject());
+
+    JSAutoByteString bytes;
+    if (const char* chars = ValueToSourceForError(cx, v, bytes)) {
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT_ARG,
+                                   nth, fun, chars);
+    }
+}
+
+void
+js::ReportNotObjectWithName(JSContext* cx, const char* name, HandleValue v)
+{
+    MOZ_ASSERT(!v.isObject());
+
+    JSAutoByteString bytes;
+    if (const char* chars = ValueToSourceForError(cx, v, bytes)) {
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT_NAME,
+                                   name, chars);
+    }
+}
+
 JS_PUBLIC_API(const char*)
 JS::InformalValueTypeName(const Value& v)
 {
     if (v.isObject())
         return v.toObject().getClass()->name;
     if (v.isString())
         return "string";
     if (v.isSymbol())
@@ -270,17 +295,17 @@ CheckCallable(JSContext* cx, JSObject* o
     return Ok();
 }
 
 bool
 js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors,
                          MutableHandle<PropertyDescriptor> desc)
 {
     // step 2
-    RootedObject obj(cx, NonNullObject(cx, descval));
+    RootedObject obj(cx, NonNullObjectWithName(cx, "property descriptor", descval));
     if (!obj)
         return false;
 
     // step 3
     desc.clear();
 
     bool found = false;
     RootedId id(cx);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1334,16 +1334,49 @@ NonNullObject(JSContext* cx, const Value
 {
     if (v.isObject())
         return &v.toObject();
     ReportNotObject(cx, v);
     return nullptr;
 }
 
 
+/*
+ * Report a TypeError: "N-th argument of FUN must be an object, got VALUE".
+ * Using NotNullObjectArg is usually less code.
+ */
+extern void
+ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun, HandleValue v);
+
+inline JSObject*
+NonNullObjectArg(JSContext* cx, const char* nth, const char* fun, HandleValue v)
+{
+    if (v.isObject())
+        return &v.toObject();
+    ReportNotObjectArg(cx, nth, fun, v);
+    return nullptr;
+}
+
+/*
+ * Report a TypeError: "SOMETHING must be an object, got VALUE".
+ * Using NotNullObjectWithName is usually less code.
+ */
+extern void
+ReportNotObjectWithName(JSContext* cx, const char* name, HandleValue v);
+
+inline JSObject*
+NonNullObjectWithName(JSContext* cx, const char* name, HandleValue v)
+{
+    if (v.isObject())
+        return &v.toObject();
+    ReportNotObjectWithName(cx, name, v);
+    return nullptr;
+}
+
+
 extern bool
 GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method,
                          MutableHandleObject objp);
 
 /* Helpers for throwing. These always return false. */
 extern bool
 Throw(JSContext* cx, jsid id, unsigned errorNumber);
 
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -652,17 +652,17 @@ ScriptedProxyHandler::defineProperty(JSC
 }
 
 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
 // 7.3.17 CreateListFromArrayLike with elementTypes fixed to symbol/string.
 static bool
 CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v, AutoIdVector& props)
 {
     // Step 2.
-    RootedObject obj(cx, NonNullObject(cx, v));
+    RootedObject obj(cx, NonNullObjectWithName(cx, "return value of the ownKeys trap", v));
     if (!obj)
         return false;
 
     // Step 3.
     uint32_t len;
     if (!GetLengthProperty(cx, obj, &len))
         return false;
 
@@ -1314,28 +1314,28 @@ ProxyCreate(JSContext* cx, CallArgs& arg
 {
     if (args.length() < 2) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
                                   callerName, "1", "s");
         return false;
     }
 
     // Step 1.
-    RootedObject target(cx, NonNullObject(cx, args[0]));
+    RootedObject target(cx, NonNullObjectArg(cx, "`target`", callerName, args[0]));
     if (!target)
         return false;
 
     // Step 2.
     if (IsRevokedScriptedProxy(target)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_ARG_REVOKED, "1");
         return false;
     }
 
     // Step 3.
-    RootedObject handler(cx, NonNullObject(cx, args[1]));
+    RootedObject handler(cx, NonNullObjectArg(cx, "`handler`", callerName, args[1]));
     if (!handler)
         return false;
 
     // Step 4.
     if (IsRevokedScriptedProxy(handler)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_ARG_REVOKED, "2");
         return false;
     }
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -115,30 +115,32 @@ VectorMatchPairs::allocOrExpandArray(siz
     pairs_ = &vec_[0];
     pairCount_ = pairCount;
     return true;
 }
 
 /* RegExpObject */
 
 static inline void
-MaybeTraceRegExpShared(JSContext* cx, RegExpShared* shared)
+RegExpSharedReadBarrier(JSContext* cx, RegExpShared* shared)
 {
     Zone* zone = cx->zone();
     if (zone->needsIncrementalBarrier())
         shared->trace(zone->barrierTracer());
+    if (shared->isMarkedGray())
+        shared->unmarkGray();
 }
 
 /* static */ bool
 RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
 {
     if (RegExpShared* shared = regexp->maybeShared()) {
         // Fetching a RegExpShared from an object requires a read
         // barrier, as the shared pointer might be weak.
-        MaybeTraceRegExpShared(cx, shared);
+        RegExpSharedReadBarrier(cx, shared);
 
         g->init(*shared);
         return true;
     }
 
     return createShared(cx, regexp, g);
 }
 
@@ -956,20 +958,40 @@ RegExpShared::~RegExpShared()
 
 void
 RegExpShared::trace(JSTracer* trc)
 {
     if (trc->isMarkingTracer())
         marked_ = true;
 
     TraceNullableEdge(trc, &source, "RegExpShared source");
+    for (auto& comp : compilationArray)
+        TraceNullableEdge(trc, &comp.jitCode, "RegExpShared code");
+}
 
-    for (size_t i = 0; i < ArrayLength(compilationArray); i++) {
-        RegExpCompilation& compilation = compilationArray[i];
-        TraceNullableEdge(trc, &compilation.jitCode, "RegExpShared code");
+bool
+RegExpShared::isMarkedGray() const
+{
+    if (source && source->isMarked(gc::GRAY))
+        return true;
+    for (const auto& comp : compilationArray) {
+        if (comp.jitCode && comp.jitCode->isMarked(gc::GRAY))
+            return true;
+    }
+    return false;
+}
+
+void
+RegExpShared::unmarkGray()
+{
+    if (source)
+        JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(source));
+    for (const auto& comp : compilationArray) {
+        if (comp.jitCode)
+            JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(comp.jitCode.get()));
     }
 }
 
 bool
 RegExpShared::compile(JSContext* cx, HandleLinearString input,
                       CompilationMode mode, ForceByteCodeEnum force)
 {
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
@@ -1338,33 +1360,33 @@ RegExpCompartment::sweep(JSRuntime* rt)
 bool
 RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g)
 {
     Key key(source, flags);
     Set::AddPtr p = set_.lookupForAdd(key);
     if (p) {
         // Trigger a read barrier on existing RegExpShared instances fetched
         // from the table (which only holds weak references).
-        MaybeTraceRegExpShared(cx, *p);
+        RegExpSharedReadBarrier(cx, *p);
 
         g->init(**p);
         return true;
     }
 
     ScopedJSDeletePtr<RegExpShared> shared(cx->new_<RegExpShared>(source, flags));
     if (!shared)
         return false;
 
     if (!set_.add(p, shared)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     // Trace RegExpShared instances created during an incremental GC.
-    MaybeTraceRegExpShared(cx, shared);
+    RegExpSharedReadBarrier(cx, shared);
 
     g->init(*shared.forget());
     return true;
 }
 
 bool
 RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt, RegExpGuard* g)
 {
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -205,16 +205,19 @@ class RegExpShared
 
     void trace(JSTracer* trc);
     bool needsSweep(JSRuntime* rt);
     void discardJitCode();
 
     bool marked() const { return marked_; }
     void clearMarked() { marked_ = false; }
 
+    bool isMarkedGray() const;
+    void unmarkGray();
+
     static size_t offsetOfSource() {
         return offsetof(RegExpShared, source);
     }
 
     static size_t offsetOfFlags() {
         return offsetof(RegExpShared, flags);
     }
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -146,16 +146,27 @@ intrinsic_ToString(JSContext* cx, unsign
     str = ToString<CanGC>(cx, args[0]);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
+intrinsic_ToSource(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JSString* str = ValueToSource(cx, args[0]);
+    if (!str)
+        return false;
+    args.rval().setString(str);
+    return true;
+}
+
+static bool
 intrinsic_ToPropertyKey(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedId id(cx);
     if (!ToPropertyKey(cx, args[0], &id))
         return false;
 
     args.rval().set(IdToValue(id));
@@ -2375,16 +2386,17 @@ static const JSFunctionSpec intrinsic_fu
     // Helper funtions after this point.
     JS_INLINABLE_FN("ToObject",      intrinsic_ToObject,                1,0, IntrinsicToObject),
     JS_INLINABLE_FN("IsObject",      intrinsic_IsObject,                1,0, IntrinsicIsObject),
     JS_INLINABLE_FN("IsArray",       intrinsic_IsArray,                 1,0, ArrayIsArray),
     JS_INLINABLE_FN("IsWrappedArrayConstructor", intrinsic_IsWrappedArrayConstructor, 1,0,
                     IntrinsicIsWrappedArrayConstructor),
     JS_INLINABLE_FN("ToInteger",     intrinsic_ToInteger,               1,0, IntrinsicToInteger),
     JS_INLINABLE_FN("ToString",      intrinsic_ToString,                1,0, IntrinsicToString),
+    JS_FN("ToSource",                intrinsic_ToSource,                1,0),
     JS_FN("ToPropertyKey",           intrinsic_ToPropertyKey,           1,0),
     JS_INLINABLE_FN("IsCallable",    intrinsic_IsCallable,              1,0, IntrinsicIsCallable),
     JS_INLINABLE_FN("IsConstructor", intrinsic_IsConstructor,           1,0,
                     IntrinsicIsConstructor),
     JS_FN("IsFunctionObject",intrinsic_IsInstanceOfBuiltin<JSFunction>, 1,0),
     JS_FN("GetBuiltinConstructorImpl", intrinsic_GetBuiltinConstructor, 1,0),
     JS_FN("MakeConstructible",       intrinsic_MakeConstructible,       2,0),
     JS_FN("_ConstructFunction",      intrinsic_ConstructFunction,       2,0),
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -37,17 +37,17 @@ AutoKeepShapeTables::~AutoKeepShapeTable
 }
 
 inline
 StackBaseShape::StackBaseShape(JSContext* cx, const Class* clasp, uint32_t objectFlags)
   : flags(objectFlags),
     clasp(clasp)
 {}
 
-inline Shape*
+MOZ_ALWAYS_INLINE Shape*
 Shape::search(JSContext* cx, jsid id)
 {
     return search(cx, this, id);
 }
 
 MOZ_ALWAYS_INLINE bool
 Shape::maybeCreateTableForLookup(JSContext* cx)
 {
@@ -80,17 +80,17 @@ Shape::search(JSContext* cx, Shape* star
     }
 
     *pentry = nullptr;
     *pshape = Shape::search<Adding>(cx, start, id);
     return true;
 }
 
 template<MaybeAdding Adding>
-/* static */ inline Shape*
+/* static */ MOZ_ALWAYS_INLINE Shape*
 Shape::search(JSContext* cx, Shape* start, jsid id)
 {
     if (start->maybeCreateTableForLookup(cx)) {
         JS::AutoCheckCannotGC nogc;
         if (ShapeTable* table = start->maybeTable(nogc)) {
             ShapeTable::Entry& entry = table->search<Adding>(id, nogc);
             return entry.shape();
         }
@@ -200,11 +200,138 @@ GetPropertyAttributes(JSObject* obj, Pro
         if (obj->is<TypedArrayObject>())
             return JSPROP_ENUMERATE | JSPROP_PERMANENT;
         return obj->as<NativeObject>().getElementsHeader()->elementAttributes();
     }
 
     return prop.shape()->attributes();
 }
 
+/*
+ * Double hashing needs the second hash code to be relatively prime to table
+ * size, so we simply make hash2 odd.
+ */
+MOZ_ALWAYS_INLINE HashNumber
+Hash1(HashNumber hash0, uint32_t shift)
+{
+    return hash0 >> shift;
+}
+
+MOZ_ALWAYS_INLINE HashNumber
+Hash2(HashNumber hash0, uint32_t log2, uint32_t shift)
+{
+    return ((hash0 << log2) >> shift) | 1;
+}
+
+template<MaybeAdding Adding>
+MOZ_ALWAYS_INLINE ShapeTable::Entry&
+ShapeTable::searchUnchecked(jsid id)
+{
+    MOZ_ASSERT(entries_);
+    MOZ_ASSERT(!JSID_IS_EMPTY(id));
+
+    /* Compute the primary hash address. */
+    HashNumber hash0 = HashId(id);
+    HashNumber hash1 = Hash1(hash0, hashShift_);
+    Entry* entry = &getEntry(hash1);
+
+    /* Miss: return space for a new entry. */
+    if (entry->isFree())
+        return *entry;
+
+    /* Hit: return entry. */
+    Shape* shape = entry->shape();
+    if (shape && shape->propidRaw() == id)
+        return *entry;
+
+    /* Collision: double hash. */
+    uint32_t sizeLog2 = HASH_BITS - hashShift_;
+    HashNumber hash2 = Hash2(hash0, sizeLog2, hashShift_);
+    uint32_t sizeMask = JS_BITMASK(sizeLog2);
+
+    /* Save the first removed entry pointer so we can recycle it if adding. */
+    Entry* firstRemoved;
+    if (Adding == MaybeAdding::Adding) {
+        if (entry->isRemoved()) {
+            firstRemoved = entry;
+        } else {
+            firstRemoved = nullptr;
+            if (!entry->hadCollision())
+                entry->flagCollision();
+        }
+    }
+
+#ifdef DEBUG
+    bool collisionFlag = true;
+    if (!entry->isRemoved())
+        collisionFlag = entry->hadCollision();
+#endif
+
+    while (true) {
+        hash1 -= hash2;
+        hash1 &= sizeMask;
+        entry = &getEntry(hash1);
+
+        if (entry->isFree())
+            return (Adding == MaybeAdding::Adding && firstRemoved) ? *firstRemoved : *entry;
+
+        shape = entry->shape();
+        if (shape && shape->propidRaw() == id) {
+            MOZ_ASSERT(collisionFlag);
+            return *entry;
+        }
+
+        if (Adding == MaybeAdding::Adding) {
+            if (entry->isRemoved()) {
+                if (!firstRemoved)
+                    firstRemoved = entry;
+            } else {
+                if (!entry->hadCollision())
+                    entry->flagCollision();
+            }
+        }
+
+#ifdef DEBUG
+        if (!entry->isRemoved())
+            collisionFlag &= entry->hadCollision();
+#endif
+    }
+
+    MOZ_CRASH("Shape::search failed to find an expected entry.");
+}
+
+template<MaybeAdding Adding>
+MOZ_ALWAYS_INLINE ShapeTable::Entry&
+ShapeTable::search(jsid id, const AutoKeepShapeTables&)
+{
+    return searchUnchecked<Adding>(id);
+}
+
+template<MaybeAdding Adding>
+MOZ_ALWAYS_INLINE ShapeTable::Entry&
+ShapeTable::search(jsid id, const JS::AutoCheckCannotGC&)
+{
+    return searchUnchecked<Adding>(id);
+}
+
+/*
+ * Keep this function in sync with search. It neither hashifies the start
+ * shape nor increments linear search count.
+ */
+MOZ_ALWAYS_INLINE Shape*
+Shape::searchNoHashify(Shape* start, jsid id)
+{
+    /*
+     * If we have a table, search in the shape table, else do a linear
+     * search. We never hashify into a table in parallel.
+     */
+    JS::AutoCheckCannotGC nogc;
+    if (ShapeTable* table = start->maybeTable(nogc)) {
+        ShapeTable::Entry& entry = table->search<MaybeAdding::NotAdding>(id, nogc);
+        return entry.shape();
+    }
+
+    return start->searchLinear(id);
+}
+
 } /* namespace js */
 
 #endif /* vm_Shape_inl_h */
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -166,112 +166,16 @@ Shape::hashify(JSContext* cx, Shape* sha
         js_free(table);
         return false;
     }
 
     shape->base()->setTable(table);
     return true;
 }
 
-/*
- * Double hashing needs the second hash code to be relatively prime to table
- * size, so we simply make hash2 odd.
- */
-static HashNumber
-Hash1(HashNumber hash0, uint32_t shift)
-{
-    return hash0 >> shift;
-}
-
-static HashNumber
-Hash2(HashNumber hash0, uint32_t log2, uint32_t shift)
-{
-    return ((hash0 << log2) >> shift) | 1;
-}
-
-template<MaybeAdding Adding>
-ShapeTable::Entry&
-ShapeTable::searchUnchecked(jsid id)
-{
-    MOZ_ASSERT(entries_);
-    MOZ_ASSERT(!JSID_IS_EMPTY(id));
-
-    /* Compute the primary hash address. */
-    HashNumber hash0 = HashId(id);
-    HashNumber hash1 = Hash1(hash0, hashShift_);
-    Entry* entry = &getEntry(hash1);
-
-    /* Miss: return space for a new entry. */
-    if (entry->isFree())
-        return *entry;
-
-    /* Hit: return entry. */
-    Shape* shape = entry->shape();
-    if (shape && shape->propidRaw() == id)
-        return *entry;
-
-    /* Collision: double hash. */
-    uint32_t sizeLog2 = HASH_BITS - hashShift_;
-    HashNumber hash2 = Hash2(hash0, sizeLog2, hashShift_);
-    uint32_t sizeMask = JS_BITMASK(sizeLog2);
-
-    /* Save the first removed entry pointer so we can recycle it if adding. */
-    Entry* firstRemoved;
-    if (Adding == MaybeAdding::Adding) {
-        if (entry->isRemoved()) {
-            firstRemoved = entry;
-        } else {
-            firstRemoved = nullptr;
-            if (!entry->hadCollision())
-                entry->flagCollision();
-        }
-    }
-
-#ifdef DEBUG
-    bool collisionFlag = true;
-    if (!entry->isRemoved())
-        collisionFlag = entry->hadCollision();
-#endif
-
-    while (true) {
-        hash1 -= hash2;
-        hash1 &= sizeMask;
-        entry = &getEntry(hash1);
-
-        if (entry->isFree())
-            return (Adding == MaybeAdding::Adding && firstRemoved) ? *firstRemoved : *entry;
-
-        shape = entry->shape();
-        if (shape && shape->propidRaw() == id) {
-            MOZ_ASSERT(collisionFlag);
-            return *entry;
-        }
-
-        if (Adding == MaybeAdding::Adding) {
-            if (entry->isRemoved()) {
-                if (!firstRemoved)
-                    firstRemoved = entry;
-            } else {
-                if (!entry->hadCollision())
-                    entry->flagCollision();
-            }
-        }
-
-#ifdef DEBUG
-        if (!entry->isRemoved())
-            collisionFlag &= entry->hadCollision();
-#endif
-    }
-
-    MOZ_CRASH("Shape::search failed to find an expected entry.");
-}
-
-template ShapeTable::Entry& ShapeTable::searchUnchecked<MaybeAdding::Adding>(jsid id);
-template ShapeTable::Entry& ShapeTable::searchUnchecked<MaybeAdding::NotAdding>(jsid id);
-
 bool
 ShapeTable::change(JSContext* cx, int log2Delta)
 {
     MOZ_ASSERT(entries_);
     MOZ_ASSERT(-1 <= log2Delta && log2Delta <= 1);
 
     /*
      * Grow, shrink, or compress by changing this->entries_.
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -196,17 +196,17 @@ class ShapeTable {
 
     uint32_t        freeList_;          /* SHAPE_INVALID_SLOT or head of slot
                                            freelist in owning dictionary-mode
                                            object */
 
     Entry*          entries_;          /* table of ptrs to shared tree nodes */
 
     template<MaybeAdding Adding>
-    Entry& searchUnchecked(jsid id);
+    MOZ_ALWAYS_INLINE Entry& searchUnchecked(jsid id);
 
   public:
     explicit ShapeTable(uint32_t nentries)
       : hashShift_(HASH_BITS - MIN_SIZE_LOG2),
         entryCount_(nentries),
         removedCount_(0),
         freeList_(SHAPE_INVALID_SLOT),
         entries_(nullptr)
@@ -233,24 +233,20 @@ class ShapeTable {
 
     // init() is fallible and reports OOM to the context.
     bool init(JSContext* cx, Shape* lastProp);
 
     // change() is fallible but does not report OOM.
     bool change(JSContext* cx, int log2Delta);
 
     template<MaybeAdding Adding>
-    MOZ_ALWAYS_INLINE Entry& search(jsid id, const AutoKeepShapeTables&) {
-        return searchUnchecked<Adding>(id);
-    }
+    MOZ_ALWAYS_INLINE Entry& search(jsid id, const AutoKeepShapeTables&);
 
     template<MaybeAdding Adding>
-    MOZ_ALWAYS_INLINE Entry& search(jsid id, const JS::AutoCheckCannotGC&) {
-        return searchUnchecked<Adding>(id);
-    }
+    MOZ_ALWAYS_INLINE Entry& search(jsid id, const JS::AutoCheckCannotGC&);
 
     void trace(JSTracer* trc);
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkAfterMovingGC();
 #endif
 
   private:
     Entry& getEntry(uint32_t i) const {
@@ -648,17 +644,17 @@ class Shape : public gc::TenuredCell
                                      to many-kids data structure */
         GCPtrShape* listp;        /* dictionary list starting at shape_
                                      has a double-indirect back pointer,
                                      either to the next shape's parent if not
                                      last, else to obj->shape_ */
     };
 
     template<MaybeAdding Adding = MaybeAdding::NotAdding>
-    static inline Shape* search(JSContext* cx, Shape* start, jsid id);
+    static MOZ_ALWAYS_INLINE Shape* search(JSContext* cx, Shape* start, jsid id);
 
     template<MaybeAdding Adding = MaybeAdding::NotAdding>
     static inline MOZ_MUST_USE bool search(JSContext* cx, Shape* start, jsid id,
                                            const AutoKeepShapeTables&,
                                            Shape** pshape, ShapeTable::Entry** pentry);
 
     static inline Shape* searchNoHashify(Shape* start, jsid id);
 
@@ -1041,17 +1037,17 @@ class Shape : public gc::TenuredCell
     void sweep();
     void finalize(FreeOp* fop);
     void removeChild(Shape* child);
 
     static const JS::TraceKind TraceKind = JS::TraceKind::Shape;
 
     void traceChildren(JSTracer* trc);
 
-    inline Shape* search(JSContext* cx, jsid id);
+    MOZ_ALWAYS_INLINE Shape* search(JSContext* cx, jsid id);
     MOZ_ALWAYS_INLINE Shape* searchLinear(jsid id);
 
     void fixupAfterMovingGC();
     void fixupGetterSetterForBarrier(JSTracer* trc);
     void updateBaseShapeAfterMovingGC();
 
     /* For JIT usage */
     static inline size_t offsetOfBase() { return offsetof(Shape, base_); }
@@ -1512,36 +1508,16 @@ Shape::searchLinear(jsid id)
         if (shape->propidRef() == id)
             return shape;
         shape = shape->parent;
     }
 
     return nullptr;
 }
 
-/*
- * Keep this function in sync with search. It neither hashifies the start
- * shape nor increments linear search count.
- */
-inline Shape*
-Shape::searchNoHashify(Shape* start, jsid id)
-{
-    /*
-     * If we have a table, search in the shape table, else do a linear
-     * search. We never hashify into a table in parallel.
-     */
-    JS::AutoCheckCannotGC nogc;
-    if (ShapeTable* table = start->maybeTable(nogc)) {
-        ShapeTable::Entry& entry = table->search<MaybeAdding::NotAdding>(id, nogc);
-        return entry.shape();
-    }
-
-    return start->searchLinear(id);
-}
-
 inline bool
 Shape::matches(const StackShape& other) const
 {
     return propid_.get() == other.propid &&
            matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags,
                                 other.rawGetter, other.rawSetter);
 }
 
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -3411,37 +3411,60 @@ class BaseCompiler
             }
         }
         return 0;
 #else
         return 0;
 #endif
     }
 
+    MOZ_MUST_USE bool needTlsForAccess(bool omitBoundsCheck) {
+#if defined(JS_CODEGEN_ARM)
+        return !omitBoundsCheck;
+#elif defined(JS_CODEGEN_X86)
+        return true;
+#else
+        return false;
+#endif
+    }
+
     // ptr and dest may be the same iff dest is I32.
     // This may destroy ptr even if ptr and dest are not the same.
-    MOZ_MUST_USE bool load(MemoryAccessDesc* access, RegI32 ptr, bool omitBoundsCheck, AnyReg dest,
-                           RegI32 tmp1, RegI32 tmp2, RegI32 tmp3)
+    MOZ_MUST_USE bool load(MemoryAccessDesc* access, RegI32 tls, RegI32 ptr, bool omitBoundsCheck,
+                           AnyReg dest, RegI32 tmp1, RegI32 tmp2, RegI32 tmp3)
     {
         checkOffset(access, ptr);
 
-        OutOfLineCode* ool = nullptr;
+#ifdef WASM_HUGE_MEMORY
+        // We have HeapReg and no bounds checking and need load neither
+        // memoryBase nor boundsCheckLimit from tls.
+        MOZ_ASSERT(tls == invalidI32());
+#endif
+#ifdef JS_CODEGEN_ARM
+        // We have HeapReg on ARM and don't need to load the memoryBase from tls.
+        MOZ_ASSERT_IF(omitBoundsCheck, tls == invalidI32());
+#endif
+
 #ifndef WASM_HUGE_MEMORY
-        if (!omitBoundsCheck)
-            masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr, trap(Trap::OutOfBounds));
+        if (!omitBoundsCheck) {
+            masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr,
+                                 Address(tls, offsetof(TlsData, boundsCheckLimit)),
+                                 trap(Trap::OutOfBounds));
+        }
 #endif
 
 #if defined(JS_CODEGEN_X64)
         Operand srcAddr(HeapReg, ptr, TimesOne, access->offset());
 
         if (dest.tag == AnyReg::I64)
             masm.wasmLoadI64(*access, srcAddr, dest.i64());
         else
             masm.wasmLoad(*access, srcAddr, dest.any());
 #elif defined(JS_CODEGEN_X86)
+        masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr);
         Operand srcAddr(ptr, access->offset());
 
         if (dest.tag == AnyReg::I64) {
             MOZ_ASSERT(dest.i64() == abiReturnRegI64);
             masm.wasmLoadI64(*access, srcAddr, dest.i64());
         } else {
             bool byteRegConflict = access->byteSize() == 1 && !singleByteRegs_.has(dest.i32());
             AnyRegister out = byteRegConflict ? AnyRegister(ScratchRegX86) : dest.any();
@@ -3472,50 +3495,61 @@ class BaseCompiler
                 masm.wasmLoadI64(*access, ptr, ptr, dest.i64());
             else
                 masm.wasmLoad(*access, ptr, ptr, dest.any());
         }
 #else
         MOZ_CRASH("BaseCompiler platform hook: load");
 #endif
 
-        if (ool)
-            masm.bind(ool->rejoin());
         return true;
     }
 
     MOZ_MUST_USE size_t storeTemps(const MemoryAccessDesc& access, ValType srcType) {
 #if defined(JS_CODEGEN_ARM)
         if (IsUnaligned(access) && srcType != ValType::I32)
             return 1;
 #endif
         return 0;
     }
 
     // ptr and src must not be the same register.
     // This may destroy ptr and src.
-    MOZ_MUST_USE bool store(MemoryAccessDesc* access, RegI32 ptr, bool omitBoundsCheck, AnyReg src,
-                            RegI32 tmp)
+    MOZ_MUST_USE bool store(MemoryAccessDesc* access, RegI32 tls, RegI32 ptr, bool omitBoundsCheck,
+                            AnyReg src, RegI32 tmp)
     {
         checkOffset(access, ptr);
 
-        Label rejoin;
+#ifdef WASM_HUGE_MEMORY
+        // We have HeapReg and no bounds checking and need load neither
+        // memoryBase nor boundsCheckLimit from tls.
+        MOZ_ASSERT(tls == invalidI32());
+#endif
+#ifdef JS_CODEGEN_ARM
+        // We have HeapReg on ARM and don't need to load the memoryBase from tls.
+        MOZ_ASSERT_IF(omitBoundsCheck, tls == invalidI32());
+#endif
+
 #ifndef WASM_HUGE_MEMORY
-        if (!omitBoundsCheck)
-            masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr, trap(Trap::OutOfBounds));
+        if (!omitBoundsCheck) {
+            masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr,
+                                 Address(tls, offsetof(TlsData, boundsCheckLimit)),
+                                 trap(Trap::OutOfBounds));
+        }
 #endif
 
         // Emit the store
 #if defined(JS_CODEGEN_X64)
         MOZ_ASSERT(tmp == Register::Invalid());
         Operand dstAddr(HeapReg, ptr, TimesOne, access->offset());
 
         masm.wasmStore(*access, src.any(), dstAddr);
 #elif defined(JS_CODEGEN_X86)
         MOZ_ASSERT(tmp == Register::Invalid());
+        masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr);
         Operand dstAddr(ptr, access->offset());
 
         if (access->type() == Scalar::Int64) {
             masm.wasmStoreI64(*access, src.i64(), dstAddr);
         } else {
             AnyRegister value;
             if (src.tag == AnyReg::I64) {
                 if (access->byteSize() == 1 && !singleByteRegs_.has(src.i64().low)) {
@@ -3558,22 +3592,49 @@ class BaseCompiler
                 masm.wasmStore(*access, AnyRegister(src.i64().low), ptr, ptr);
             else
                 masm.wasmStore(*access, src.any(), ptr, ptr);
         }
 #else
         MOZ_CRASH("BaseCompiler platform hook: store");
 #endif
 
-        if (rejoin.used())
-            masm.bind(&rejoin);
-
         return true;
     }
 
+    MOZ_MUST_USE bool
+    supportsRoundInstruction(RoundingMode mode)
+    {
+#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
+        return Assembler::HasRoundInstruction(mode);
+#else
+        return false;
+#endif
+    }
+
+    void
+    roundF32(RoundingMode roundingMode, RegF32 f0)
+    {
+#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
+        masm.vroundss(Assembler::ToX86RoundingMode(roundingMode), f0, f0, f0);
+#else
+        MOZ_CRASH("NYI");
+#endif
+    }
+
+    void
+    roundF64(RoundingMode roundingMode, RegF64 f0)
+    {
+#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
+        masm.vroundsd(Assembler::ToX86RoundingMode(roundingMode), f0, f0, f0);
+#else
+        MOZ_CRASH("NYI");
+#endif
+    }
+
     ////////////////////////////////////////////////////////////
 
     // Generally speaking, ABOVE this point there should be no value
     // stack manipulation (calls to popI32 etc).
 
     // Generally speaking, BELOW this point there should be no
     // platform dependencies.  We make an exception for x86 register
     // targeting, which is not too hard to keep clean.
@@ -3784,16 +3845,17 @@ class BaseCompiler
     MOZ_MUST_USE bool emitCall();
     MOZ_MUST_USE bool emitCallIndirect();
     MOZ_MUST_USE bool emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandType);
     MOZ_MUST_USE bool emitGetLocal();
     MOZ_MUST_USE bool emitSetLocal();
     MOZ_MUST_USE bool emitTeeLocal();
     MOZ_MUST_USE bool emitGetGlobal();
     MOZ_MUST_USE bool emitSetGlobal();
+    MOZ_MUST_USE RegI32 maybeLoadTlsForAccess(bool omitBoundsCheck);
     MOZ_MUST_USE bool emitLoad(ValType type, Scalar::Type viewType);
     MOZ_MUST_USE bool emitStore(ValType resultType, Scalar::Type viewType);
     MOZ_MUST_USE bool emitSelect();
 
     // Mark these templates as inline to work around a compiler crash in
     // gcc 4.8.5 when compiling for linux64-opt.
 
     template<bool isSetLocal> MOZ_MUST_USE inline bool emitSetOrTeeLocal(uint32_t slot);
@@ -3899,16 +3961,17 @@ class BaseCompiler
 #else
     void emitConvertI64ToF32();
     void emitConvertU64ToF32();
     void emitConvertI64ToF64();
     void emitConvertU64ToF64();
 #endif
     void emitReinterpretI32AsF32();
     void emitReinterpretI64AsF64();
+    void emitRound(RoundingMode roundingMode, ValType operandType);
     MOZ_MUST_USE bool emitGrowMemory();
     MOZ_MUST_USE bool emitCurrentMemory();
 };
 
 void
 BaseCompiler::emitAddI32()
 {
     int32_t c;
@@ -5874,28 +5937,50 @@ BaseCompiler::emitCallIndirect()
     popValueStackBy(numArgs);
 
     if (!IsVoid(sig.ret()))
         pushReturned(baselineCall, sig.ret());
 
     return true;
 }
 
+void
+BaseCompiler::emitRound(RoundingMode roundingMode, ValType operandType)
+{
+    if (operandType == ValType::F32) {
+        RegF32 f0 = popF32();
+        roundF32(roundingMode, f0);
+        pushF32(f0);
+    } else if (operandType == ValType::F64) {
+        RegF64 f0 = popF64();
+        roundF64(roundingMode, f0);
+        pushF64(f0);
+    } else {
+        MOZ_CRASH("unexpected type");
+    }
+}
+
 bool
 BaseCompiler::emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandType)
 {
     uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
     Nothing operand_;
     if (!iter_.readUnary(operandType, &operand_))
         return false;
 
     if (deadCode_)
         return true;
 
+    RoundingMode roundingMode;
+    if (IsRoundingFunction(callee, &roundingMode) && supportsRoundInstruction(roundingMode)) {
+        emitRound(roundingMode, operandType);
+        return true;
+    }
+
     sync();
 
     ValTypeVector& signature = operandType == ValType::F32 ? SigF_ : SigD_;
     ExprType retType = operandType == ValType::F32 ? ExprType::F32 : ExprType::F64;
     uint32_t numArgs = signature.length();
     size_t stackSpace = stackConsumed(numArgs);
 
     FunctionCall baselineCall(lineOrBytecode);
@@ -6349,43 +6434,57 @@ BaseCompiler::popMemoryAccess(MemoryAcce
 
     uint32_t local;
     if (peekLocalI32(&local))
         bceCheckLocal(access, local, omitBoundsCheck);
 
     return popI32();
 }
 
+BaseCompiler::RegI32
+BaseCompiler::maybeLoadTlsForAccess(bool omitBoundsCheck)
+{
+    RegI32 tls = invalidI32();
+    if (needTlsForAccess(omitBoundsCheck)) {
+        tls = needI32();
+        loadFromFramePtr(tls, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
+    }
+    return tls;
+}
+
 bool
 BaseCompiler::emitLoad(ValType type, Scalar::Type viewType)
 {
     LinearMemoryAddress<Nothing> addr;
     if (!iter_.readLoad(type, Scalar::byteSize(viewType), &addr))
         return false;
 
     if (deadCode_)
         return true;
 
     bool omitBoundsCheck = false;
     MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(trapOffset()));
 
     size_t temps = loadTemps(access);
+    MOZ_ASSERT(temps <= 3);
     RegI32 tmp1 = temps >= 1 ? needI32() : invalidI32();
     RegI32 tmp2 = temps >= 2 ? needI32() : invalidI32();
     RegI32 tmp3 = temps >= 3 ? needI32() : invalidI32();
+    RegI32 tls = invalidI32();
 
     switch (type) {
       case ValType::I32: {
         RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
 #ifdef JS_CODEGEN_ARM
         RegI32 rv = IsUnaligned(access) ? needI32() : rp;
 #else
         RegI32 rv = rp;
 #endif
-        if (!load(&access, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
+        tls = maybeLoadTlsForAccess(omitBoundsCheck);
+        if (!load(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
             return false;
         pushI32(rv);
         if (rp != rv)
             freeI32(rp);
         break;
       }
       case ValType::I64: {
         RegI64 rv;
@@ -6393,45 +6492,52 @@ BaseCompiler::emitLoad(ValType type, Sca
 #ifdef JS_CODEGEN_X86
         rv = abiReturnRegI64;
         needI64(rv);
         rp = popMemoryAccess(&access, &omitBoundsCheck);
 #else
         rp = popMemoryAccess(&access, &omitBoundsCheck);
         rv = needI64();
 #endif
-        if (!load(&access, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
+        tls = maybeLoadTlsForAccess(omitBoundsCheck);
+        if (!load(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
             return false;
         pushI64(rv);
         freeI32(rp);
         break;
       }
       case ValType::F32: {
         RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
         RegF32 rv = needF32();
-        if (!load(&access, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
+        tls = maybeLoadTlsForAccess(omitBoundsCheck);
+        if (!load(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
             return false;
         pushF32(rv);
         freeI32(rp);
         break;
       }
       case ValType::F64: {
         RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
         RegF64 rv = needF64();
-        if (!load(&access, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
+        tls = maybeLoadTlsForAccess(omitBoundsCheck);
+        if (!load(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
             return false;
         pushF64(rv);
         freeI32(rp);
         break;
       }
       default:
         MOZ_CRASH("load type");
         break;
     }
 
+    if (tls != invalidI32())
+        freeI32(tls);
+
+    MOZ_ASSERT(temps <= 3);
     if (temps >= 1)
         freeI32(tmp1);
     if (temps >= 2)
         freeI32(tmp2);
     if (temps >= 3)
         freeI32(tmp3);
 
     return true;
@@ -6450,59 +6556,67 @@ BaseCompiler::emitStore(ValType resultTy
 
     bool omitBoundsCheck = false;
     MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(trapOffset()));
 
     size_t temps = storeTemps(access, resultType);
 
     MOZ_ASSERT(temps <= 1);
     RegI32 tmp = temps >= 1 ? needI32() : invalidI32();
+    RegI32 tls = invalidI32();
 
     switch (resultType) {
       case ValType::I32: {
         RegI32 rv = popI32();
         RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
-        if (!store(&access, rp, omitBoundsCheck, AnyReg(rv), tmp))
+        tls = maybeLoadTlsForAccess(omitBoundsCheck);
+        if (!store(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp))
             return false;
         freeI32(rp);
         freeI32(rv);
         break;
       }
       case ValType::I64: {
         RegI64 rv = popI64();
         RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
-        if (!store(&access, rp, omitBoundsCheck, AnyReg(rv), tmp))
+        tls = maybeLoadTlsForAccess(omitBoundsCheck);
+        if (!store(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp))
             return false;
         freeI32(rp);
         freeI64(rv);
         break;
       }
       case ValType::F32: {
         RegF32 rv = popF32();
         RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
-        if (!store(&access, rp, omitBoundsCheck, AnyReg(rv), tmp))
+        tls = maybeLoadTlsForAccess(omitBoundsCheck);
+        if (!store(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp))
             return false;
         freeI32(rp);
         freeF32(rv);
         break;
       }
       case ValType::F64: {
         RegF64 rv = popF64();
         RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
-        if (!store(&access, rp, omitBoundsCheck, AnyReg(rv), tmp))
+        tls = maybeLoadTlsForAccess(omitBoundsCheck);
+        if (!store(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp))
             return false;
         freeI32(rp);
         freeF64(rv);
         break;
       }
       default:
         MOZ_CRASH("store type");
         break;
     }
 
+    if (tls != invalidI32())
+        freeI32(tls);
+
     MOZ_ASSERT(temps <= 1);
     if (temps >= 1)
         freeI32(tmp);
 
     return true;
 }
 
 bool
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -115,50 +115,16 @@ StaticallyLink(CodeSegment& cs, const Li
             Assembler::PatchDataWithValueCheck(CodeLocationLabel(patchAt),
                                                PatchedImmPtr(target),
                                                PatchedImmPtr((void*)-1));
         }
     }
 }
 
 static void
-SpecializeToMemory(uint8_t* prevMemoryBase, CodeSegment& cs, const Metadata& metadata,
-                   ArrayBufferObjectMaybeShared& buffer)
-{
-#ifdef WASM_HUGE_MEMORY
-    MOZ_RELEASE_ASSERT(metadata.boundsChecks.empty());
-#else
-    uint32_t limit = buffer.wasmBoundsCheckLimit();
-    MOZ_RELEASE_ASSERT(IsValidBoundsCheckImmediate(limit));
-
-    for (const BoundsCheck& check : metadata.boundsChecks)
-        MacroAssembler::wasmPatchBoundsCheck(check.patchAt(cs.base()), limit);
-#endif
-
-#if defined(JS_CODEGEN_X86)
-    uint8_t* memoryBase = buffer.dataPointerEither().unwrap(/* code patching */);
-    if (prevMemoryBase != memoryBase) {
-        for (MemoryPatch patch : metadata.memoryPatches) {
-            void* patchAt = cs.base() + patch.offset;
-
-            uint8_t* prevImm = (uint8_t*)X86Encoding::GetPointer(patchAt);
-            MOZ_ASSERT(prevImm >= prevMemoryBase);
-
-            uint32_t offset = prevImm - prevMemoryBase;
-            MOZ_ASSERT(offset <= INT32_MAX);
-
-            X86Encoding::SetPointer(patchAt, memoryBase + offset);
-        }
-    }
-#else
-    MOZ_RELEASE_ASSERT(metadata.memoryPatches.empty());
-#endif
-}
-
-static void
 SendCodeRangesToProfiler(CodeSegment& cs, const Bytes& bytecode, const Metadata& metadata)
 {
     bool enabled = false;
 #ifdef JS_ION_PERF
     enabled |= PerfFuncEnabled();
 #endif
 #ifdef MOZ_VTUNE
     enabled |= vtune::IsProfilingActive();
@@ -236,18 +202,16 @@ CodeSegment::create(JSContext* cx,
 
     {
         JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
         AutoFlushICache afc("CodeSegment::create");
         AutoFlushICache::setRange(uintptr_t(codeBase), cs->length());
 
         memcpy(codeBase, bytecode.begin(), bytecode.length());
         StaticallyLink(*cs, linkData, cx);
-        if (memory)
-            SpecializeToMemory(nullptr, *cs, metadata, memory->buffer());
     }
 
     // Reprotect the whole region to avoid having separate RW and RX mappings.
     uint32_t size = JS_ROUNDUP(cs->length(), ExecutableCodePageSize);
     if (!ExecutableAllocator::makeExecutable(codeBase, size)) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
@@ -270,26 +234,16 @@ CodeSegment::~CodeSegment()
     // Match AllocateCodeSegment.
     uint32_t size = JS_ROUNDUP(length(), ExecutableCodePageSize);
 #ifdef MOZ_VTUNE
     vtune::UnmarkBytes(bytes_, size);
 #endif
     DeallocateExecutableMemory(bytes_, size);
 }
 
-void
-CodeSegment::onMovingGrow(uint8_t* prevMemoryBase, const Metadata& metadata, ArrayBufferObject& buffer)
-{
-    AutoWritableJitCode awjc(base(), length());
-    AutoFlushICache afc("CodeSegment::onMovingGrow");
-    AutoFlushICache::setRange(uintptr_t(base()), length());
-
-    SpecializeToMemory(prevMemoryBase, *this, metadata, buffer);
-}
-
 size_t
 FuncExport::serializedSize() const
 {
     return sig_.serializedSize() +
            sizeof(pod);
 }
 
 uint8_t*
@@ -452,18 +406,16 @@ Metadata::serializedSize() const
 {
     return sizeof(pod()) +
            SerializedVectorSize(funcImports) +
            SerializedVectorSize(funcExports) +
            SerializedVectorSize(sigIds) +
            SerializedPodVectorSize(globals) +
            SerializedPodVectorSize(tables) +
            SerializedPodVectorSize(memoryAccesses) +
-           SerializedPodVectorSize(memoryPatches) +
-           SerializedPodVectorSize(boundsChecks) +
            SerializedPodVectorSize(codeRanges) +
            SerializedPodVectorSize(callSites) +
            SerializedPodVectorSize(callThunks) +
            SerializedPodVectorSize(funcNames) +
            SerializedPodVectorSize(customSections) +
            filename.serializedSize();
 }
 
@@ -475,18 +427,16 @@ Metadata::serialize(uint8_t* cursor) con
                debugFuncToCodeRange.empty());
     cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
     cursor = SerializeVector(cursor, funcImports);
     cursor = SerializeVector(cursor, funcExports);
     cursor = SerializeVector(cursor, sigIds);
     cursor = SerializePodVector(cursor, globals);
     cursor = SerializePodVector(cursor, tables);
     cursor = SerializePodVector(cursor, memoryAccesses);
-    cursor = SerializePodVector(cursor, memoryPatches);
-    cursor = SerializePodVector(cursor, boundsChecks);
     cursor = SerializePodVector(cursor, codeRanges);
     cursor = SerializePodVector(cursor, callSites);
     cursor = SerializePodVector(cursor, callThunks);
     cursor = SerializePodVector(cursor, funcNames);
     cursor = SerializePodVector(cursor, customSections);
     cursor = filename.serialize(cursor);
     return cursor;
 }
@@ -496,18 +446,16 @@ Metadata::deserialize(const uint8_t* cur
 {
     (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
     (cursor = DeserializeVector(cursor, &funcImports)) &&
     (cursor = DeserializeVector(cursor, &funcExports)) &&
     (cursor = DeserializeVector(cursor, &sigIds)) &&
     (cursor = DeserializePodVector(cursor, &globals)) &&
     (cursor = DeserializePodVector(cursor, &tables)) &&
     (cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
-    (cursor = DeserializePodVector(cursor, &memoryPatches)) &&
-    (cursor = DeserializePodVector(cursor, &boundsChecks)) &&
     (cursor = DeserializePodVector(cursor, &codeRanges)) &&
     (cursor = DeserializePodVector(cursor, &callSites)) &&
     (cursor = DeserializePodVector(cursor, &callThunks)) &&
     (cursor = DeserializePodVector(cursor, &funcNames)) &&
     (cursor = DeserializePodVector(cursor, &customSections)) &&
     (cursor = filename.deserialize(cursor));
     debugEnabled = false;
     debugTrapFarJumpOffsets.clear();
@@ -521,18 +469,16 @@ size_t
 Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
            SizeOfVectorExcludingThis(funcExports, mallocSizeOf) +
            SizeOfVectorExcludingThis(sigIds, mallocSizeOf) +
            globals.sizeOfExcludingThis(mallocSizeOf) +
            tables.sizeOfExcludingThis(mallocSizeOf) +
            memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
-           memoryPatches.sizeOfExcludingThis(mallocSizeOf) +
-           boundsChecks.sizeOfExcludingThis(mallocSizeOf) +
            codeRanges.sizeOfExcludingThis(mallocSizeOf) +
            callSites.sizeOfExcludingThis(mallocSizeOf) +
            callThunks.sizeOfExcludingThis(mallocSizeOf) +
            funcNames.sizeOfExcludingThis(mallocSizeOf) +
            customSections.sizeOfExcludingThis(mallocSizeOf) +
            filename.sizeOfExcludingThis(mallocSizeOf);
 }
 
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -96,21 +96,16 @@ class CodeSegment
     // enter/exit.
 
     bool containsFunctionPC(const void* pc) const {
         return pc >= base() && pc < (base() + functionLength_);
     }
     bool containsCodePC(const void* pc) const {
         return pc >= base() && pc < (base() + length_);
     }
-
-    // onMovingGrow must be called if the memory passed to 'create' performs a
-    // moving grow operation.
-
-    void onMovingGrow(uint8_t* prevMemoryBase, const Metadata& metadata, ArrayBufferObject& buffer);
 };
 
 // ShareableBytes is a ref-counted vector of bytes which are incrementally built
 // during compilation and then immutably shared.
 
 struct ShareableBytes : ShareableBase<ShareableBytes>
 {
     // Vector is 'final', so instead make Vector a member and add boilerplate.
@@ -461,18 +456,16 @@ struct Metadata : ShareableBase<Metadata
     const MetadataCacheablePod& pod() const { return *this; }
 
     FuncImportVector      funcImports;
     FuncExportVector      funcExports;
     SigWithIdVector       sigIds;
     GlobalDescVector      globals;
     TableDescVector       tables;
     MemoryAccessVector    memoryAccesses;
-    MemoryPatchVector     memoryPatches;
-    BoundsCheckVector     boundsChecks;
     CodeRangeVector       codeRanges;
     CallSiteVector        callSites;
     CallThunkVector       callThunks;
     NameInBytecodeVector  funcNames;
     CustomSectionVector   customSections;
     CacheableChars        filename;
 
     // Debug-enabled code is not serialized.
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -1144,18 +1144,16 @@ ModuleGenerator::finish(const ShareableB
 
     // Convert the CallSiteAndTargetVector (needed during generation) to a
     // CallSiteVector (what is stored in the Module).
     if (!metadata_->callSites.appendAll(masm_.callSites()))
         return nullptr;
 
     // The MacroAssembler has accumulated all the memory accesses during codegen.
     metadata_->memoryAccesses = masm_.extractMemoryAccesses();
-    metadata_->memoryPatches = masm_.extractMemoryPatches();
-    metadata_->boundsChecks = masm_.extractBoundsChecks();
 
     // Copy over data from the ModuleEnvironment.
     metadata_->memoryUsage = env_->memoryUsage;
     metadata_->minMemoryLength = env_->minMemoryLength;
     metadata_->maxMemoryLength = env_->maxMemoryLength;
     metadata_->tables = Move(env_->tables);
     metadata_->globals = Move(env_->globals);
     metadata_->funcNames = Move(env_->funcNames);
@@ -1165,18 +1163,16 @@ ModuleGenerator::finish(const ShareableB
     metadata_->debugFuncArgTypes = Move(debugFuncArgTypes_);
     metadata_->debugFuncReturnTypes = Move(debugFuncReturnTypes_);
     if (metadata_->debugEnabled)
         metadata_->debugFuncToCodeRange = Move(funcToCodeRange_);
 
     // These Vectors can get large and the excess capacity can be significant,
     // so realloc them down to size.
     metadata_->memoryAccesses.podResizeToFit();
-    metadata_->memoryPatches.podResizeToFit();
-    metadata_->boundsChecks.podResizeToFit();
     metadata_->codeRanges.podResizeToFit();
     metadata_->callSites.podResizeToFit();
     metadata_->callThunks.podResizeToFit();
     metadata_->debugTrapFarJumpOffsets.podResizeToFit();
     metadata_->debugFuncToCodeRange.podResizeToFit();
 
     // For asm.js, the tables vector is over-allocated (to avoid resize during
     // parallel copilation). Shrink it back down to fit.
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -333,16 +333,19 @@ Instance::Instance(JSContext* cx,
 {
     MOZ_ASSERT(funcImports.length() == metadata().funcImports.length());
     MOZ_ASSERT(tables_.length() == metadata().tables.length());
 
     tlsData()->cx = cx;
     tlsData()->instance = this;
     tlsData()->globalData = globals_->globalData();
     tlsData()->memoryBase = memory ? memory->buffer().dataPointerEither().unwrap() : nullptr;
+#ifndef WASM_HUGE_MEMORY
+    tlsData()->boundsCheckLimit = memory ? memory->buffer().wasmBoundsCheckLimit() : 0;
+#endif
     tlsData()->stackLimit = *(void**)cx->stackLimitAddressForJitCode(JS::StackForUntrustedScript);
 
     for (size_t i = 0; i < metadata().funcImports.length(); i++) {
         HandleFunction f = funcImports[i];
         const FuncImport& fi = metadata().funcImports[i];
         FuncImportTls& import = funcImportTls(fi);
         if (!isAsmJS() && IsExportedWasmFunction(f)) {
             WasmInstanceObject* calleeInstanceObj = ExportedFunctionToInstanceObject(f);
@@ -750,17 +753,19 @@ Instance::callExport(JSContext* cx, uint
 }
 
 void
 Instance::onMovingGrowMemory(uint8_t* prevMemoryBase)
 {
     MOZ_ASSERT(!isAsmJS());
     ArrayBufferObject& buffer = memory_->buffer().as<ArrayBufferObject>();
     tlsData()->memoryBase = buffer.dataPointer();
-    code_->segment().onMovingGrow(prevMemoryBase, metadata(), buffer);
+#ifndef WASM_HUGE_MEMORY
+    tlsData()->boundsCheckLimit = buffer.wasmBoundsCheckLimit();
+#endif
 }
 
 void
 Instance::onMovingGrowTable()
 {
     MOZ_ASSERT(!isAsmJS());
     MOZ_ASSERT(tables_.length() == 1);
     TableTls& table = tableTls(metadata().tables[0]);
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -410,16 +410,26 @@ class FunctionCompiler
             return nullptr;
 
         // wasm can't fold x - 0.0 because of NaN with custom payloads.
         MSub* ins = MSub::New(alloc(), lhs, rhs, type, mustPreserveNaN(type));
         curBlock_->add(ins);
         return ins;
     }
 
+    MDefinition* nearbyInt(MDefinition* input, RoundingMode roundingMode)
+    {
+        if (inDeadCode())
+            return nullptr;
+
+        auto* ins = MNearbyInt::New(alloc(), input, input->type(), roundingMode);
+        curBlock_->add(ins);
+        return ins;
+    }
+
     MDefinition* unarySimd(MDefinition* input, MSimdUnaryArith::Operation op, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
 
         MOZ_ASSERT(IsSimdType(input->type()) && input->type() == type);
         MInstruction* ins = MSimdUnaryArith::New(alloc(), input, op);
         curBlock_->add(ins);
@@ -696,102 +706,133 @@ class FunctionCompiler
     void assign(unsigned slot, MDefinition* def)
     {
         if (inDeadCode())
             return;
         curBlock_->setSlot(info().localSlot(slot), def);
     }
 
   private:
+    MWasmLoadTls* maybeLoadMemoryBase() {
+        MWasmLoadTls* load = nullptr;
+#ifdef JS_CODEGEN_X86
+        AliasSet aliases = env_.maxMemoryLength.isSome() ? AliasSet::None()
+                                                         : AliasSet::Load(AliasSet::WasmHeapMeta);
+        load = MWasmLoadTls::New(alloc(), tlsPointer_, offsetof(wasm::TlsData, memoryBase),
+                                 MIRType::Pointer, aliases);
+        curBlock_->add(load);
+#endif
+        return load;
+    }
+
+    MWasmLoadTls* maybeLoadBoundsCheckLimit() {
+        MWasmLoadTls* load = nullptr;
+#ifndef WASM_HUGE_MEMORY
+        AliasSet aliases = env_.maxMemoryLength.isSome() ? AliasSet::None()
+                                                         : AliasSet::Load(AliasSet::WasmHeapMeta);
+        load = MWasmLoadTls::New(alloc(), tlsPointer_, offsetof(wasm::TlsData, boundsCheckLimit),
+                                 MIRType::Int32, aliases);
+        curBlock_->add(load);
+#endif
+        return load;
+    }
+
     void checkOffsetAndBounds(MemoryAccessDesc* access, MDefinition** base)
     {
         // If the offset is bigger than the guard region, a separate instruction
         // is necessary to add the offset to the base and check for overflow.
         if (access->offset() >= OffsetGuardLimit || !JitOptions.wasmFoldOffsets) {
             auto* ins = MWasmAddOffset::New(alloc(), *base, access->offset(), trapOffset());
             curBlock_->add(ins);
 
             *base = ins;
             access->clearOffset();
         }
 
-#ifndef WASM_HUGE_MEMORY
-        curBlock_->add(MWasmBoundsCheck::New(alloc(), *base, trapOffset()));
-#endif
+        MWasmLoadTls* boundsCheckLimit = maybeLoadBoundsCheckLimit();
+        if (boundsCheckLimit)
+            curBlock_->add(MWasmBoundsCheck::New(alloc(), *base, boundsCheckLimit, trapOffset()));
     }
 
   public:
     MDefinition* load(MDefinition* base, MemoryAccessDesc* access, ValType result)
     {
         if (inDeadCode())
             return nullptr;
 
+        MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
         MInstruction* load = nullptr;
         if (access->isPlainAsmJS()) {
             MOZ_ASSERT(access->offset() == 0);
-            load = MAsmJSLoadHeap::New(alloc(), base, access->type());
+            MWasmLoadTls* boundsCheckLimit = maybeLoadBoundsCheckLimit();
+            load = MAsmJSLoadHeap::New(alloc(), memoryBase, base, boundsCheckLimit, access->type());
         } else {
             checkOffsetAndBounds(access, &base);
-            load = MWasmLoad::New(alloc(), base, *access, ToMIRType(result));
+            load = MWasmLoad::New(alloc(), memoryBase, base, *access, ToMIRType(result));
         }
 
         curBlock_->add(load);
         return load;
     }
 
     void store(MDefinition* base, MemoryAccessDesc* access, MDefinition* v)
     {
         if (inDeadCode())
             return;
 
+        MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
         MInstruction* store = nullptr;
         if (access->isPlainAsmJS()) {
             MOZ_ASSERT(access->offset() == 0);
-            store = MAsmJSStoreHeap::New(alloc(), base, access->type(), v);
+            MWasmLoadTls* boundsCheckLimit = maybeLoadBoundsCheckLimit();
+            store = MAsmJSStoreHeap::New(alloc(), memoryBase, base, boundsCheckLimit, access->type(), v);
         } else {
             checkOffsetAndBounds(access, &base);
-            store = MWasmStore::New(alloc(), base, *access, v);
+            store = MWasmStore::New(alloc(), memoryBase, base, *access, v);
         }
 
         curBlock_->add(store);
     }
 
     MDefinition* atomicCompareExchangeHeap(MDefinition* base, MemoryAccessDesc* access,
                                            MDefinition* oldv, MDefinition* newv)
     {
         if (inDeadCode())
             return nullptr;
 
         checkOffsetAndBounds(access, &base);
-        auto* cas = MAsmJSCompareExchangeHeap::New(alloc(), base, *access, oldv, newv, tlsPointer_);
+        MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
+        auto* cas = MAsmJSCompareExchangeHeap::New(alloc(), memoryBase, base, *access, oldv, newv, tlsPointer_);
         curBlock_->add(cas);
         return cas;
     }
 
     MDefinition* atomicExchangeHeap(MDefinition* base, MemoryAccessDesc* access,
                                     MDefinition* value)
     {
         if (inDeadCode())
             return nullptr;
 
         checkOffsetAndBounds(access, &base);
-        auto* cas = MAsmJSAtomicExchangeHeap::New(alloc(), base, *access, value, tlsPointer_);
+        MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
+        auto* cas = MAsmJSAtomicExchangeHeap::New(alloc(), memoryBase, base, *access, value, tlsPointer_);
         curBlock_->add(cas);
         return cas;
     }
 
     MDefinition* atomicBinopHeap(js::jit::AtomicOp op,
                                  MDefinition* base, MemoryAccessDesc* access,
                                  MDefinition* v)
     {
         if (inDeadCode())
             return nullptr;
 
         checkOffsetAndBounds(access, &base);
-        auto* binop = MAsmJSAtomicBinopHeap::New(alloc(), op, base, *access, v, tlsPointer_);
+        MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
+        auto* binop = MAsmJSAtomicBinopHeap::New(alloc(), op, memoryBase, base, *access, v, tlsPointer_);
         curBlock_->add(binop);
         return binop;
     }
 
     MDefinition* loadGlobalVar(unsigned globalDataOffset, bool isConst, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
@@ -2333,28 +2374,50 @@ EmitTeeStoreWithCoercion(FunctionCompile
 
     MemoryAccessDesc access(viewType, addr.align, addr.offset, f.trapIfNotAsmJS());
 
     f.store(addr.base, &access, value);
     return true;
 }
 
 static bool
+TryInlineUnaryBuiltin(FunctionCompiler& f, SymbolicAddress callee, MDefinition* input)
+{
+    if (!input)
+        return false;
+
+    MOZ_ASSERT(IsFloatingPointType(input->type()));
+
+    RoundingMode mode;
+    if (!IsRoundingFunction(callee, &mode))
+        return false;
+
+    if (!MNearbyInt::HasAssemblerSupport(mode))
+        return false;
+
+    f.iter().setResult(f.nearbyInt(input, mode));
+    return true;
+}
+
+static bool
 EmitUnaryMathBuiltinCall(FunctionCompiler& f, SymbolicAddress callee, ValType operandType)
 {
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
 
+    MDefinition* input;
+    if (!f.iter().readUnary(operandType, &input))
+        return false;
+
+    if (TryInlineUnaryBuiltin(f, callee, input))
+        return true;
+
     CallCompileState call(f, lineOrBytecode);
     if (!f.startCall(&call))
         return false;
 
-    MDefinition* input;
-    if (!f.iter().readUnary(operandType, &input))
-        return false;
-
     if (!f.passArg(input, operandType, &call))
         return false;
 
     if (!f.finishCall(&call, TlsUsage::Unused))
         return false;
 
     MDefinition* def;
     if (!f.builtinCall(callee, call, operandType, &def))
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -376,16 +376,41 @@ FuncCast(F* pf, ABIFunctionType type)
 {
     void *pv = JS_FUNC_TO_DATA_PTR(void*, pf);
 #ifdef JS_SIMULATOR
     pv = Simulator::RedirectNativeFunction(pv, type);
 #endif
     return pv;
 }
 
+bool
+wasm::IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode)
+{
+    switch (callee) {
+      case SymbolicAddress::FloorD:
+      case SymbolicAddress::FloorF:
+        *mode = jit::RoundingMode::Down;
+        return true;
+      case SymbolicAddress::CeilD:
+      case SymbolicAddress::CeilF:
+        *mode = jit::RoundingMode::Up;
+        return true;
+      case SymbolicAddress::TruncD:
+      case SymbolicAddress::TruncF:
+        *mode = jit::RoundingMode::TowardsZero;
+        return true;
+      case SymbolicAddress::NearbyIntD:
+      case SymbolicAddress::NearbyIntF:
+        *mode = jit::RoundingMode::NearestTiesToEven;
+        return true;
+      default:
+        return false;
+    }
+}
+
 void*
 wasm::AddressOf(SymbolicAddress imm, JSContext* cx)
 {
     switch (imm) {
       case SymbolicAddress::ContextPtr:
         return cx->zone()->group()->addressOfOwnerContext();
       case SymbolicAddress::ReportOverRecursed:
         return FuncCast(WasmReportOverRecursed, Args_General0);
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -36,17 +36,20 @@
 #include "js/Utility.h"
 #include "js/Vector.h"
 #include "vm/MallocProvider.h"
 #include "wasm/WasmBinaryConstants.h"
 
 namespace js {
 
 class PropertyName;
-namespace jit { struct BaselineScript; }
+namespace jit {
+    struct BaselineScript;
+    enum class RoundingMode;
+}
 
 // This is a widespread header, so lets keep out the core wasm impl types.
 
 class WasmMemoryObject;
 typedef GCPtr<WasmMemoryObject*> GCPtrWasmMemoryObject;
 typedef Rooted<WasmMemoryObject*> RootedWasmMemoryObject;
 typedef Handle<WasmMemoryObject*> HandleWasmMemoryObject;
 typedef MutableHandle<WasmMemoryObject*> MutableHandleWasmMemoryObject;
@@ -1039,16 +1042,19 @@ enum class SymbolicAddress
     Uint64ToDouble,
     Int64ToFloat32,
     Int64ToDouble,
     GrowMemory,
     CurrentMemory,
     Limit
 };
 
+bool
+IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode);
+
 void*
 AddressOf(SymbolicAddress imm, JSContext* cx);
 
 // Assumptions captures ambient state that must be the same when compiling and
 // deserializing a module for the compiled code to be valid. If it's not, then
 // the module must be recompiled from scratch.
 
 struct Assumptions
@@ -1148,16 +1154,21 @@ struct TlsData
     Instance* instance;
 
     // Pointer to the global data for this Instance.
     uint8_t* globalData;
 
     // Pointer to the base of the default memory (or null if there is none).
     uint8_t* memoryBase;
 
+#ifndef WASM_HUGE_MEMORY
+    // Bounds check limit of memory, in bytes (or zero if there is no memory).
+    uint32_t boundsCheckLimit;
+#endif
+
     // Stack limit for the current thread. This limit is checked against the
     // stack pointer in the prologue of functions that allocate stack space. See
     // `CodeGenerator::generateWasm`.
     void* stackLimit;
 
     // The globalArea must be the last field.  Globals for the module start here
     // and are inline in this structure.  16-byte alignment is required for SIMD
     // data.
@@ -1421,42 +1432,16 @@ IsValidBoundsCheckImmediate(uint32_t i);
 //   boundsCheckLimit = mappedSize - GuardSize
 //   IsValidBoundsCheckImmediate(boundsCheckLimit)
 
 extern size_t
 ComputeMappedSize(uint32_t maxSize);
 
 #endif // WASM_HUGE_MEMORY
 
-// Metadata for bounds check instructions that are patched at runtime with the
-// appropriate bounds check limit. On WASM_HUGE_MEMORY platforms for wasm (and
-// SIMD/Atomic) bounds checks, no BoundsCheck is created: the signal handler
-// catches everything. On !WASM_HUGE_MEMORY, a BoundsCheck is created for each
-// memory access (except when statically eliminated by optimizations) so that
-// the length can be patched in as an immediate. This requires that the bounds
-// check limit IsValidBoundsCheckImmediate.
-
-class BoundsCheck
-{
-  public:
-    BoundsCheck() = default;
-
-    explicit BoundsCheck(uint32_t cmpOffset)
-      : cmpOffset_(cmpOffset)
-    { }
-
-    uint8_t* patchAt(uint8_t* code) const { return code + cmpOffset_; }
-    void offsetBy(uint32_t offset) { cmpOffset_ += offset; }
-
-  private:
-    uint32_t cmpOffset_;
-};
-
-WASM_DECLARE_POD_VECTOR(BoundsCheck, BoundsCheckVector)
-
 // Metadata for memory accesses. On WASM_HUGE_MEMORY platforms, only
 // (non-SIMD/Atomic) asm.js loads and stores create a MemoryAccess so that the
 // signal handler can implement the semantically-correct wraparound logic; the
 // rest simply redirect to the out-of-bounds stub in the signal handler. On x86,
 // the base address of memory is baked into each memory access instruction so
 // the MemoryAccess records the location of each for patching. On all other
 // platforms, no MemoryAccess is created.
 
@@ -1487,36 +1472,16 @@ class MemoryAccess
         insnOffset_ += delta;
         if (hasTrapOutOfLineCode())
             trapOutOfLineOffset_ += delta;
     }
 };
 
 WASM_DECLARE_POD_VECTOR(MemoryAccess, MemoryAccessVector)
 
-// Metadata for the offset of an instruction to patch with the base address of
-// memory. In practice, this is only used for x86 where the offset points to the
-// *end* of the instruction (which is a non-fixed offset from the beginning of
-// the instruction). As part of the move away from code patching, this should be
-// removed.
-
-struct MemoryPatch
-{
-    uint32_t offset;
-
-    MemoryPatch() = default;
-    explicit MemoryPatch(uint32_t offset) : offset(offset) {}
-
-    void offsetBy(uint32_t delta) {
-        offset += delta;
-    }
-};
-
-WASM_DECLARE_POD_VECTOR(MemoryPatch, MemoryPatchVector)
-
 // As an invariant across architectures, within wasm code:
 //   $sp % WasmStackAlignment = (sizeof(wasm::Frame) + masm.framePushed) % WasmStackAlignment
 // Thus, wasm::Frame represents the bytes pushed after the call (which occurred
 // with a WasmStackAlignment-aligned StackPointer) that are not included in
 // masm.framePushed.
 
 struct Frame
 {
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -18,28 +18,28 @@ asserts-if(stylo,1) load 397022-1.html #
 load 399289-1.svg
 load 404470-1.html
 load 411603-1.html
 load 412588-1.html
 load 413274-1.xhtml
 load 416461-1.xul
 load 418007-1.xhtml
 load 431705-1.xul
-load 432561-1.html # bug 1323705
+load 432561-1.html
 load 437170-1.html
 load 437532-1.html
-load 439184-1.html # bug 1323939
+load 439184-1.html
 load 444237-1.html
 load 444848-1.html 
 load 447776-1.html
 load 447783-1.html
 load 448161-1.html
 load 448161-2.html
 load 452150-1.xhtml
-load 456196.html # bug 132652
+load 456196.html
 load 460209-1.html
 load 460217-1.html
 load 460323-1.html
 load 466845-1.html
 load 469432-1.xhtml
 load 472195-1.html
 load 472237-1.html # will fail, test for leak (474704)
 HTTP(..) load 472237-1.html
@@ -76,17 +76,17 @@ load 621596-1.html
 skip-if(stylo) load 622314-1.xhtml # bug 1337695
 load 637242.xhtml
 load 645142.html
 fails-if(stylo) == 645951-1.html 645951-1-ref.html
 load 652976-1.svg
 load 665209-1.html
 load 671799-1.html
 load 671799-2.html
-load 690990-1.html # bug 1323708
+load 690990-1.html
 load 696188-1.html
 load 696869-1.html
 asserts-if(stylo,2) load 700116.html # bug 1324635
 load 729126-1.html
 load 729126-2.html
 load 786108-1.html
 load 786108-2.html
 load 788836.html
@@ -110,17 +110,17 @@ load 930270-1.html
 load 930270-2.html
 load 945048-1.html
 load 972199-1.html
 load 989965-1.html
 load 992333-1.html
 skip-if(stylo) pref(dom.webcomponents.enabled,true) load 1017798-1.html # bug 1323689
 load 1028514-1.html
 load 1066089-1.html
-load 1074651-1.html # bug 1323652
+load 1074651-1.html
 load 1135534.html
 pref(dom.webcomponents.enabled,true) load 1089463-1.html
 pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1136010-1.html
 asserts-if(stylo,0-2) pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1146101-1.html # bug 1324677
 load 1153693-1.html
 load 1161320-1.html
 pref(dom.animations-api.core.enabled,true) load 1161320-2.html
 load 1161366-1.html
@@ -148,17 +148,17 @@ load border-image-visited-link.html
 load font-face-truncated-src.html 
 load large_border_image_width.html
 load long-url-list-stack-overflow.html
 pref(layout.css.background-clip-text.enabled,true) load 1264949.html
 pref(layout.css.background-clip-text.enabled,true) load 1270795.html
 pref(layout.css.background-clip-text.enabled,true) load 1275026.html
 load 1278463-1.html
 pref(dom.animations-api.core.enabled,true) load 1277908-1.html # bug 1323652
-load 1277908-2.html # bug 1323652
+load 1277908-2.html
 load 1282076-1.html
 pref(dom.animations-api.core.enabled,true) load 1282076-2.html
 pref(dom.animations-api.core.enabled,true) load 1290994-1.html
 pref(dom.animations-api.core.enabled,true) load 1290994-2.html
 pref(dom.animations-api.core.enabled,true) load 1290994-3.html
 load 1290994-4.html
 load 1314531.html
 load 1315889-1.html
--- a/layout/tools/reftest/reftest.jsm
+++ b/layout/tools/reftest/reftest.jsm
@@ -272,33 +272,21 @@ this.OnRefTestLoad = function OnRefTestL
                     .get("ProfD", CI.nsIFile);
     gCrashDumpDir.append("minidumps");
 
     var env = CC["@mozilla.org/process/environment;1"].
               getService(CI.nsIEnvironment);
 
     var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                 getService(Components.interfaces.nsIPrefBranch);
-    try {
-        gBrowserIsRemote = prefs.getBoolPref("browser.tabs.remote.autostart");
-    } catch (e) {
-        gBrowserIsRemote = false;
-    }
+    gBrowserIsRemote = prefs.getBoolPref("browser.tabs.remote.autostart", false);
 
-    try {
-      gBrowserIsIframe = prefs.getBoolPref("reftest.browser.iframe.enabled");
-    } catch (e) {
-      gBrowserIsIframe = false;
-    }
+    gBrowserIsIframe = prefs.getBoolPref("reftest.browser.iframe.enabled", false);
 
-    try {
-      gLogLevel = prefs.getCharPref("reftest.logLevel");
-    } catch (e) {
-      gLogLevel ='info';
-    }
+    gLogLevel = prefs.getCharPref("reftest.logLevel", "info");
 
     if (win === undefined || win == null) {
       win = window;
     }
     if (gContainingWindow == null && win != null) {
       gContainingWindow = win;
     }
 
@@ -370,27 +358,19 @@ function InitAndStartRefTests()
     try {
         var logFile = prefs.getCharPref("reftest.logFile");
         if (logFile) {
             var f = FileUtils.File(logFile);
             gLogFile = FileUtils.openFileOutputStream(f, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE);
         }
     } catch(e) {}
 
-    try {
-        gRemote = prefs.getBoolPref("reftest.remote");
-    } catch(e) {
-        gRemote = false;
-    }
+    gRemote = prefs.getBoolPref("reftest.remote", false);
 
-    try {
-        gIgnoreWindowSize = prefs.getBoolPref("reftest.ignoreWindowSize");
-    } catch(e) {
-        gIgnoreWindowSize = false;
-    }
+    gIgnoreWindowSize = prefs.getBoolPref("reftest.ignoreWindowSize", false);
 
     /* Support for running a chunk (subset) of tests.  In separate try as this is optional */
     try {
         gTotalChunks = prefs.getIntPref("reftest.totalChunks");
         gThisChunk = prefs.getIntPref("reftest.thisChunk");
     }
     catch(e) {
         gTotalChunks = 0;
@@ -468,49 +448,29 @@ function StartTests()
     /* These prefs are optional, so we don't need to spit an error to the log */
     try {
         var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                     getService(Components.interfaces.nsIPrefBranch);
     } catch(e) {
         logger.error("EXCEPTION: " + e);
     }
 
-    try {
-        gNoCanvasCache = prefs.getIntPref("reftest.nocache");
-    } catch(e) {
-        gNoCanvasCache = false;
-    }
+    gNoCanvasCache = prefs.getIntPref("reftest.nocache", false);
 
-    try {
-      gShuffle = prefs.getBoolPref("reftest.shuffle");
-    } catch (e) {
-      gShuffle = false;
-    }
+    gShuffle = prefs.getBoolPref("reftest.shuffle", false);
 
-    try {
-      gRunUntilFailure = prefs.getBoolPref("reftest.runUntilFailure");
-    } catch (e) {
-      gRunUntilFailure = false;
-    }
+    gRunUntilFailure = prefs.getBoolPref("reftest.runUntilFailure", false);
 
     // When we repeat this function is called again, so really only want to set
     // gRepeat once.
     if (gRepeat == null) {
-      try {
-        gRepeat = prefs.getIntPref("reftest.repeat");
-      } catch (e) {
-        gRepeat = 0;
-      }
+      gRepeat = prefs.getIntPref("reftest.repeat", 0);
     }
 
-    try {
-        gRunSlowTests = prefs.getIntPref("reftest.skipslowtests");
-    } catch(e) {
-        gRunSlowTests = false;
-    }
+    gRunSlowTests = prefs.getIntPref("reftest.skipslowtests", false);
 
     if (gShuffle) {
         gNoCanvasCache = true;
     }
 
     gURLs = [];
     gManifestsLoaded = {};
 
@@ -749,21 +709,17 @@ function BuildConditionSandbox(aURL) {
 
     var prefs = CC["@mozilla.org/preferences-service;1"].
                 getService(CI.nsIPrefBranch);
     try {
         sandbox.nativeThemePref = !prefs.getBoolPref("mozilla.widget.disable-native-theme");
     } catch (e) {
         sandbox.nativeThemePref = true;
     }
-    try {
-        sandbox.gpuProcessForceEnabled = prefs.getBoolPref("layers.gpu-process.force-enabled");
-    } catch (e) {
-        sandbox.gpuProcessForceEnabled = false;
-    }
+    sandbox.gpuProcessForceEnabled = prefs.getBoolPref("layers.gpu-process.force-enabled", false);
 
     sandbox.prefs = CU.cloneInto({
         getBoolPref: function(p) { return prefs.getBoolPref(p); },
         getIntPref:  function(p) { return prefs.getIntPref(p); }
     }, sandbox, { cloneFunctions: true });
 
     // Tests shouldn't care about this except for when they need to
     // crash the content process
--- a/media/ffvpx/moz.build
+++ b/media/ffvpx/moz.build
@@ -1,10 +1,13 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Audio/Video: Playback")
+
 DIRS += [
     'libavutil',
     'libavcodec'
 ]
--- a/media/gmp-clearkey/0.1/moz.build
+++ b/media/gmp-clearkey/0.1/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Audio/Video: GMP")
+
 SharedLibrary('clearkey')
 
 FINAL_TARGET = 'dist/bin/gmp-clearkey/0.1'
 
 FINAL_TARGET_PP_FILES += ['manifest.json.in']
 
 UNIFIED_SOURCES += [
     'ClearKeyBase64.cpp',
--- a/media/kiss_fft/moz.build
+++ b/media/kiss_fft/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Web Audio")
+
 EXPORTS.kiss_fft += [
     'kiss_fft.h',
     'kiss_fftr.h',
 ]
 
 SOURCES += [
     'kiss_fft.c',
     'kiss_fftr.c',
--- a/media/libav/moz.build
+++ b/media/libav/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Web Audio")
+
 # Due to duplicate file names, we compile libavutil/x86/cpu.c in its own
 # moz.build file.
 DIRS += ['libavutil/x86']
 
 EXPORTS.libavcodec += [
     'libavcodec/avfft.h',
     'libavcodec/fft.h'
 ]
--- a/media/libcubeb/moz.build
+++ b/media/libcubeb/moz.build
@@ -1,9 +1,12 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Audio/Video: cubeb")
+
 DIRS += ['include', 'src']
 TEST_DIRS += ['gtest']
 
--- a/media/libjpeg/moz.build
+++ b/media/libjpeg/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "ImageLib")
+
 EXPORTS += [
     'jconfig.h',
     'jerror.h',
     'jinclude.h',
     'jmorecfg.h',
     'jpegint.h',
     'jpeglib.h',
 ]
--- a/media/libmkv/moz.build
+++ b/media/libmkv/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Audio/Video: Recording")
+
 EXPORTS.libmkv += [
     'EbmlBufferWriter.h',
     'EbmlIDs.h',
     'EbmlWriter.h',
     'WebMElement.h',
 ]
 
 UNIFIED_SOURCES += [
--- a/media/libnestegg/moz.build
+++ b/media/libnestegg/moz.build
@@ -1,8 +1,11 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Audio/Video: Playback")
+
 DIRS += ['include', 'src']
 
--- a/media/libpng/moz.build
+++ b/media/libpng/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "ImageLib")
+
 EXPORTS += [
     'png.h',
     'pngconf.h',
     'pnglibconf.h'
 ]
 
 UNIFIED_SOURCES += [
     'png.c',
--- a/media/libsoundtouch/moz.build
+++ b/media/libsoundtouch/moz.build
@@ -1,8 +1,11 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Audio/Video: Playback")
+
 DIRS += ['src']
 
--- a/media/libspeex_resampler/moz.build
+++ b/media/libspeex_resampler/moz.build
@@ -1,8 +1,11 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Web Audio")
+
 DIRS += ['src']
 
--- a/media/libstagefright/moz.build
+++ b/media/libstagefright/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Audio/Video: Playback")
+
 DEFINES['ANDROID_SMP'] = 0
 DEFINES['LOG_NDEBUG'] = 1
 
 if CONFIG['OS_TARGET'] != 'WINNT':
    DEFINES['_GLIBCXX_OS_DEFINES'] = True
 
 if CONFIG['OS_TARGET'] == 'WINNT':
     if CONFIG['_MSC_VER']:
--- a/media/libyuv/moz.build
+++ b/media/libyuv/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Graphics")
+
 include('/build/gyp.mozbuild')
 
 libyuv_non_unified_sources = [
     'libyuv/source/convert.cc',
     'libyuv/source/convert_from.cc',
     'libyuv/source/mjpeg_decoder.cc',
     'libyuv/source/rotate_argb.cc',
     'libyuv/source/row_common.cc',
new file mode 100644
--- /dev/null
+++ b/media/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Audio/Video")
+
+with Files("update-libjpeg.sh"):
+    BUG_COMPONENT = ("Core", "ImageLib")
+
+with Files("openmax_il/**"):
+    BUG_COMPONENT = ("Core", "Audio/Video: Playback")
+
+with Files("gmp-clearkey/**"):
+    BUG_COMPONENT = ("Core", "Audio/Video: GMP")
--- a/media/mtransport/moz.build
+++ b/media/mtransport/moz.build
@@ -1,12 +1,15 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "WebRTC: Networking")
+
 include("/ipc/chromium/chromium-config.mozbuild")
 
 DIRS += [
     '/media/mtransport/third_party',
     '/media/mtransport/build',
 ]
--- a/media/omx-plugin/moz.build
+++ b/media/omx-plugin/moz.build
@@ -9,16 +9,19 @@
 #     http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Audio/Video: Playback")
+
 SOURCES += [
     'OmxPlugin.cpp',
 ]
 
 SharedLibrary('omxplugin')
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     pass
--- a/media/openmax_dl/moz.build
+++ b/media/openmax_dl/moz.build
@@ -1,7 +1,10 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Web Audio")
+
 DIRS += ['dl']
\ No newline at end of file
--- a/media/pocketsphinx/moz.build
+++ b/media/pocketsphinx/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Web Speech")
+
 LOCAL_INCLUDES += [
     '/media/sphinxbase',
 ]
 
 EXPORTS.pocketsphinx += [
     'pocketsphinx.h',
 ]
 
--- a/media/sphinxbase/moz.build
+++ b/media/sphinxbase/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Web Speech")
+
 EXPORTS.sphinxbase += [
     'sphinxbase/cmd_ln.h',
     'sphinxbase/fe.h',
     'sphinxbase/feat.h',
     'sphinxbase/logmath.h',
 ]
 
 SOURCES += [
--- a/media/webrtc/moz.build
+++ b/media/webrtc/moz.build
@@ -1,14 +1,20 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "WebRTC")
+
+with Files("signaling/**"):
+    BUG_COMPONENT = ("Core", "WebRTC: Signaling")
+
 include('/build/gyp.mozbuild')
 
 webrtc_non_unified_sources = [
     'trunk/webrtc/common_audio/vad/vad_core.c',                                  # Because of name clash in the kInitCheck variable
     'trunk/webrtc/common_audio/vad/webrtc_vad.c',                                # Because of name clash in the kInitCheck variable
     'trunk/webrtc/modules/audio_coding/acm2/codec_manager.cc',                   # Because of duplicate IsCodecRED/etc
     'trunk/webrtc/modules/audio_coding/codecs/g722/g722_decode.c',               # Because of name clash in the saturate function
     'trunk/webrtc/modules/audio_coding/codecs/g722/g722_encode.c',               # Because of name clash in the saturate function
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -11,17 +11,16 @@ import android.app.DownloadManager;
 import android.content.ContentProviderClient;
 import android.os.Environment;
 import android.os.Process;
 import android.support.annotation.NonNull;
 import android.support.annotation.UiThread;
 
 import android.graphics.Rect;
 
-import org.json.JSONArray;
 import org.mozilla.gecko.activitystream.ActivityStream;
 import org.mozilla.gecko.adjust.AdjustBrowserAppDelegate;
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
 import org.mozilla.gecko.Tabs.TabEvents;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
@@ -163,18 +162,16 @@ import android.view.animation.Interpolat
 import android.widget.Button;
 import android.widget.ListView;
 import android.widget.RelativeLayout;
 import android.widget.ViewFlipper;
 import org.mozilla.gecko.switchboard.AsyncConfigLoader;
 import org.mozilla.gecko.switchboard.SwitchBoard;
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
-import org.json.JSONException;
-import org.json.JSONObject;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.net.URLEncoder;
 import java.util.Arrays;
 import java.util.Collections;
@@ -1376,23 +1373,19 @@ public class BrowserApp extends GeckoApp
             }
             return true;
         }
 
         if (itemId == R.id.subscribe) {
             // This can be selected from either the browser menu or the contextmenu, depending on the size and version (v11+) of the phone.
             Tab tab = Tabs.getInstance().getSelectedTab();
             if (tab != null && tab.hasFeeds()) {
-                JSONObject args = new JSONObject();
-                try {
-                    args.put("tabId", tab.getId());
-                } catch (JSONException e) {
-                    Log.e(LOGTAG, "error building json arguments", e);
-                }
-                GeckoAppShell.notifyObservers("Feeds:Subscribe", args.toString());
+                final GeckoBundle args = new GeckoBundle(1);
+                args.putInt("tabId", tab.getId());
+                EventDispatcher.getInstance().dispatch("Feeds:Subscribe", args);
             }
             return true;
         }
 
         if (itemId == R.id.add_search_engine) {
             // This can be selected from either the browser menu or the contextmenu, depending on the size and version (v11+) of the phone.
             Tab tab = Tabs.getInstance().getSelectedTab();
             if (tab != null && tab.hasOpenSearch()) {
@@ -1912,17 +1905,19 @@ public class BrowserApp extends GeckoApp
                     codeArray[i] = charset.getString("code");
                 }
 
                 final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
                 dialogBuilder.setSingleChoiceItems(titleArray, selected,
                         new AlertDialog.OnClickListener() {
                             @Override
                             public void onClick(final DialogInterface dialog, final int which) {
-                                GeckoAppShell.notifyObservers("CharEncoding:Set", codeArray[which]);
+                                final GeckoBundle data = new GeckoBundle(1);
+                                data.putString("encoding", codeArray[which]);
+                                EventDispatcher.getInstance().dispatch("CharEncoding:Set", data);
                                 dialog.dismiss();
                             }
                         });
                 dialogBuilder.setNegativeButton(R.string.button_cancel,
                         new AlertDialog.OnClickListener() {
                             @Override
                             public void onClick(final DialogInterface dialog, final int which) {
                                 dialog.dismiss();
@@ -1936,18 +1931,17 @@ public class BrowserApp extends GeckoApp
                 GeckoPreferences.setCharEncodingState(visible);
                 if (mMenu != null) {
                     mMenu.findItem(R.id.char_encoding).setVisible(visible);
                 }
                 break;
 
             case "Experiments:GetActive":
                 final List<String> experiments = SwitchBoard.getActiveExperiments(this);
-                final JSONArray json = new JSONArray(experiments);
-                callback.sendSuccess(json.toString());
+                callback.sendSuccess(experiments.toArray(new String[experiments.size()]));
                 break;
 
             case "Experiments:SetOverride":
                 Experiments.setOverride(getContext(), message.getString("name"),
                                         message.getBoolean("isEnabled"));
                 break;
 
             case "Experiments:ClearOverride":
@@ -3157,17 +3151,19 @@ public class BrowserApp extends GeckoApp
             }
         }
 
         final MenuItem item = destination.add(Menu.NONE, info.id, Menu.NONE, info.label);
 
         item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
             @Override
             public boolean onMenuItemClick(MenuItem item) {
-                GeckoAppShell.notifyObservers("Menu:Clicked", Integer.toString(info.id - ADDON_MENU_OFFSET));
+                final GeckoBundle data = new GeckoBundle(1);
+                data.putInt("item", info.id - ADDON_MENU_OFFSET);
+                EventDispatcher.getInstance().dispatch("Menu:Clicked", data);
                 return true;
             }
         });
 
         item.setCheckable(info.checkable);
         item.setChecked(info.checked);
         item.setEnabled(info.enabled);
         item.setVisible(info.visible);
@@ -3669,17 +3665,17 @@ public class BrowserApp extends GeckoApp
         if (itemId == R.id.history_list) {
             final String url = AboutPages.getURLForBuiltinPanelType(PanelType.COMBINED_HISTORY);
             Tabs.getInstance().loadUrl(url);
             return true;
         }
 
         if (itemId == R.id.save_as_pdf) {
             Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "pdf");
-            GeckoAppShell.notifyObservers("SaveAs:PDF", null);
+            EventDispatcher.getInstance().dispatch("SaveAs:PDF", null);
             return true;
         }
 
         if (itemId == R.id.print) {
             Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "print");
             PrintHelper.printPDF(this);
             return true;
         }
@@ -3714,37 +3710,33 @@ public class BrowserApp extends GeckoApp
         }
 
         if (itemId == R.id.downloads) {
             Tabs.getInstance().loadUrlInTab(AboutPages.DOWNLOADS);
             return true;
         }
 
         if (itemId == R.id.char_encoding) {
-            GeckoAppShell.notifyObservers("CharEncoding:Get", null);
+            EventDispatcher.getInstance().dispatch("CharEncoding:Get", null);
             return true;
         }
 
         if (itemId == R.id.find_in_page) {
             mFindInPageBar.show();
             return true;
         }
 
         if (itemId == R.id.desktop_mode) {
             Tab selectedTab = Tabs.getInstance().getSelectedTab();
             if (selectedTab == null)
                 return true;
-            JSONObject args = new JSONObject();
-            try {
-                args.put("desktopMode", !item.isChecked());
-                args.put("tabId", selectedTab.getId());
-            } catch (JSONException e) {
-                Log.e(LOGTAG, "error building json arguments", e);
-            }
-            GeckoAppShell.notifyObservers("DesktopMode:Change", args.toString());
+            final GeckoBundle args = new GeckoBundle(2);
+            args.putBoolean("desktopMode", !item.isChecked());
+            args.putInt("tabId", selectedTab.getId());
+            EventDispatcher.getInstance().dispatch("DesktopMode:Change", args);
             return true;
         }
 
         if (itemId == R.id.new_tab) {