Merge mozilla-inbound to mozilla-central
authorEd Morley <emorley@mozilla.com>
Fri, 08 Feb 2013 11:00:58 +0000
changeset 131931 fcf79680a057f2ce62f13d4c7e03c3bf2c96a7d4
parent 131869 6a2bebec5914b991979ab5abe1933728c52b0b4b (current diff)
parent 131930 e99ec356f47bb56d1d9761938f8a5f196931e9cd (diff)
child 131932 469333ea459caf1b1c859ba04b866852b367e54b
child 131942 d643b3d246fa2c92a950fd80116a98eb7102e74f
push id317
push userbbajaj@mozilla.com
push dateTue, 07 May 2013 01:20:33 +0000
treeherdermozilla-release@159a10910249 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone21.0a1
first release with
nightly linux32
fcf79680a057 / 21.0a1 / 20130208031053 / files
nightly linux64
fcf79680a057 / 21.0a1 / 20130208031053 / files
nightly mac
fcf79680a057 / 21.0a1 / 20130208031053 / files
nightly win32
fcf79680a057 / 21.0a1 / 20130208031053 / files
nightly win64
fcf79680a057 / 21.0a1 / 20130208031053 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central
browser/modules/TelemetryTimestamps.jsm
browser/modules/test/browser_TelemetryTimestamps.js
content/html/content/test/test_ol_attributes_reflection.html
content/media/AudioEventTimeline.h
dom/browser-element/BrowserElementChild.js
dom/browser-element/BrowserElementScrolling.js
mobile/android/base/resources/color/tab_title.xml
mobile/android/base/resources/color/tabs_counter_color.xml
mobile/android/base/resources/drawable-large-hdpi-v11/tabs_normal.png
mobile/android/base/resources/drawable-large-hdpi-v11/tabs_private.png
mobile/android/base/resources/drawable-large-hdpi-v11/tabs_synced.png
mobile/android/base/resources/drawable-large-mdpi-v11/tabs_normal.png
mobile/android/base/resources/drawable-large-mdpi-v11/tabs_private.png
mobile/android/base/resources/drawable-large-mdpi-v11/tabs_synced.png
mobile/android/base/resources/drawable-large-xhdpi-v11/tabs_normal.png
mobile/android/base/resources/drawable-large-xhdpi-v11/tabs_private.png
mobile/android/base/resources/drawable-large-xhdpi-v11/tabs_synced.png
toolkit/crashreporter/breakpad-patches/06-readsymboldata-mac
toolkit/crashreporter/google-breakpad/src/common/module.cc.orig
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -202,22 +202,27 @@ SettingsListener.observe('devtools.debug
   Services.prefs.setBoolPref('devtools.debugger.remote-enabled', value);
   // This preference is consulted during startup
   Services.prefs.savePrefFile(null);
   value ? RemoteDebugger.start() : RemoteDebugger.stop();
 
 #ifdef MOZ_WIDGET_GONK
   let enableAdb = value;
 
-  if (Services.prefs.getBoolPref('marionette.defaultPrefs.enabled')) {
-    // Marionette is enabled. Force adb on, since marionette requires remote
-    // debugging to be disabled (we don't want adb to track the remote debugger
-    // setting).
+  try {
+    if (Services.prefs.getBoolPref('marionette.defaultPrefs.enabled')) {
+      // Marionette is enabled. Force adb on, since marionette requires remote
+      // debugging to be disabled (we don't want adb to track the remote debugger
+      // setting).
 
-    enableAdb = true;
+      enableAdb = true;
+    }
+  } catch (e) {
+    // This means that the pref doesn't exist. Which is fine. We just leave
+    // enableAdb alone.
   }
 
   // Configure adb.
   try {
     let currentConfig = libcutils.property_get("persist.sys.usb.config");
     let configFuncs = currentConfig.split(",");
     let adbIndex = configFuncs.indexOf("adb");
 
--- a/b2g/components/Keyboard.jsm
+++ b/b2g/components/Keyboard.jsm
@@ -41,22 +41,16 @@ let Keyboard = {
     for (let name of this._messageNames)
       ppmm.addMessageListener('Keyboard:' + name, this);
   },
 
   observe: function keyboardObserve(subject, topic, data) {
     let frameLoader = subject.QueryInterface(Ci.nsIFrameLoader);
     let mm = frameLoader.messageManager;
     mm.addMessageListener('Forms:Input', this);
-
-    try {
-      mm.loadFrameScript(kFormsFrameScript, true);
-    } catch (e) {
-      dump('Error loading ' + kFormsFrameScript + ' as frame script: ' + e + '\n');
-    }
   },
 
   receiveMessage: function keyboardReceiveMessage(msg) {
     switch (msg.name) {
       case 'Forms:Input':
         this.handleFormsInput(msg);
         break;
       case 'Keyboard:SetValue':
--- a/b2g/components/ProcessGlobal.js
+++ b/b2g/components/ProcessGlobal.js
@@ -38,17 +38,16 @@ ProcessGlobal.prototype = {
   classID: Components.ID('{1a94c87a-5ece-4d11-91e1-d29c29f21b28}'),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
   observe: function pg_observe(subject, topic, data) {
     switch (topic) {
     case 'app-startup': {
       Services.obs.addObserver(this, 'console-api-log-event', false);
-      Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
       break;
     }
     case 'console-api-log-event': {
       // Pipe `console` log messages to the nsIConsoleService which
       // writes them to logcat on Gonk.
       let message = subject.wrappedJSObject;
       let prefix = ('Content JS ' + message.level.toUpperCase() +
                     ' at ' + message.filename + ':' + message.lineNumber +
--- a/b2g/components/RecoveryService.js
+++ b/b2g/components/RecoveryService.js
@@ -14,18 +14,19 @@ const RECOVERYSERVICE_CID = Components.I
 const RECOVERYSERVICE_CONTRACTID = "@mozilla.org/recovery-service;1";
 
 function log(msg) {
   dump("-*- RecoveryService: " + msg + "\n");
 }
 
 #ifdef MOZ_WIDGET_GONK
 let librecovery = (function() {
+  let library;
   try {
-    let library = ctypes.open("librecovery.so");
+    library = ctypes.open("librecovery.so");
   } catch (e) {
     log("Unable to open librecovery.so");
     throw Cr.NS_ERROR_FAILURE;
   }
   let FotaUpdateStatus = new ctypes.StructType("FotaUpdateStatus", [
                                                 { result: ctypes.int },
                                                 { updatePath: ctypes.char.ptr }
                                               ]);
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -903,18 +903,23 @@ var SocialToolbar = {
         toolbarButtons.appendChild(toolbarButtonContainer);
       }
 
       toolbarButton.style.listStyleImage = "url(" + icon.iconURL + ")";
       toolbarButton.setAttribute("label", icon.label);
       toolbarButton.setAttribute("tooltiptext", icon.label);
 
       let badge = icon.counter || "";
-      if (toolbarButton.getAttribute("badge") != badge)
-        toolbarButton.setAttribute("badge", badge);
+      toolbarButton.setAttribute("badge", badge);
+      let ariaLabel = icon.label;
+      // if there is a badge value, we must use a localizable string to insert it.
+      if (badge)
+        ariaLabel = gNavigatorBundle.getFormattedString("social.aria.toolbarButtonBadgeText",
+                                                        [ariaLabel, badge]);
+      toolbarButton.setAttribute("aria-label", ariaLabel);
     }
     let socialToolbarItem = document.getElementById("social-toolbar-item");
     socialToolbarItem.appendChild(toolbarButtons);
 
     for (let frame of createdFrames) {
       if (frame.socialErrorListener) {
         frame.socialErrorListener.remove();
       }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4676,29 +4676,34 @@ var TabsProgressListener = {
 
     // Attach a listener to watch for "click" events bubbling up from error
     // pages and other similar page. This lets us fix bugs like 401575 which
     // require error page UI to do privileged things, without letting error
     // pages have any privilege themselves.
     // We can't look for this during onLocationChange since at that point the
     // document URI is not yet the about:-uri of the error page.
 
+    let doc = aWebProgress.DOMWindow.document;
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         Components.isSuccessCode(aStatus) &&
-        aWebProgress.DOMWindow.document.documentURI.startsWith("about:")) {
+        doc.documentURI.startsWith("about:") &&
+        !doc.documentElement.hasAttribute("hasBrowserHandlers")) {
+      // STATE_STOP may be received twice for documents, thus store an
+      // attribute to ensure handling it just once.
+      doc.documentElement.setAttribute("hasBrowserHandlers", "true");
       aBrowser.addEventListener("click", BrowserOnClick, true);
       aBrowser.addEventListener("pagehide", function onPageHide(event) {
         if (event.target.defaultView.frameElement)
           return;
         aBrowser.removeEventListener("click", BrowserOnClick, true);
         aBrowser.removeEventListener("pagehide", onPageHide, true);
       }, true);
 
       // We also want to make changes to page UI for unprivileged about pages.
-      BrowserOnAboutPageLoad(aWebProgress.DOMWindow.document);
+      BrowserOnAboutPageLoad(doc);
     }
   },
 
   onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI,
                               aFlags) {
     // Filter out any sub-frame loads
     if (aBrowser.contentWindow == aWebProgress.DOMWindow) {
       // Filter out any onLocationChanges triggered by anchor navigation
--- a/browser/base/content/test/browser_bug435325.js
+++ b/browser/base/content/test/browser_bug435325.js
@@ -3,40 +3,52 @@
 
 /* Ensure that clicking the button in the Offline mode neterror page makes the browser go online. See bug 435325. */
 
 let proxyPrefValue;
 
 function test() {
   waitForExplicitFinish();
 
-  gBrowser.selectedTab = gBrowser.addTab();
-  window.addEventListener("DOMContentLoaded", checkPage, false);
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
 
   // Go offline and disable the proxy and cache, then try to load the test URL.
   Services.io.offline = true;
 
   // Tests always connect to localhost, and per bug 87717, localhost is now
   // reachable in offline mode.  To avoid this, disable any proxy.
   proxyPrefValue = Services.prefs.getIntPref("network.proxy.type");
   Services.prefs.setIntPref("network.proxy.type", 0);
 
   Services.prefs.setBoolPref("browser.cache.disk.enable", false);
   Services.prefs.setBoolPref("browser.cache.memory.enable", false);
   content.location = "http://example.com/";
+
+  window.addEventListener("DOMContentLoaded", function load() {
+    if (content.location == "about:blank") {
+      info("got about:blank, which is expected once, so return");
+      return;
+    }
+    window.removeEventListener("DOMContentLoaded", load, false);
+
+    let observer = new MutationObserver(function (mutations) {
+      for (let mutation of mutations) {
+        if (mutation.attributeName == "hasBrowserHandlers") {
+          observer.disconnect();
+          checkPage();
+          return;
+        }
+      }
+    });
+    let docElt = tab.linkedBrowser.contentDocument.documentElement;
+    observer.observe(docElt, { attributes: true });
+  }, false);
 }
 
 function checkPage() {
-  if(content.location == "about:blank") {
-    info("got about:blank, which is expected once, so return");
-    return;
-  }
-
-  window.removeEventListener("DOMContentLoaded", checkPage, false);
-
   ok(Services.io.offline, "Setting Services.io.offline to true.");
   is(gBrowser.contentDocument.documentURI.substring(0,27),
     "about:neterror?e=netOffline", "Loading the Offline mode neterror page.");
 
   // Now press the "Try Again" button
   ok(gBrowser.contentDocument.getElementById("errorTryAgain"),
     "The error page has got a #errorTryAgain element");
   gBrowser.contentDocument.getElementById("errorTryAgain").click();
--- a/browser/base/content/test/social/browser_social_toolbar.js
+++ b/browser/base/content/test/social/browser_social_toolbar.js
@@ -105,21 +105,25 @@ var tests = {
 
     let statusIcon = document.querySelector("#social-toolbar-item > .social-notification-container > .toolbarbutton-1");
     waitForCondition(function() {
       statusIcon = document.querySelector("#social-toolbar-item > .social-notification-container > .toolbarbutton-1");
       return !!statusIcon;
     }, function () {
       let badge = statusIcon.getAttribute("badge");
       is(badge, "42", "status value is correct");
+      // If there is a counter, the aria-label should reflect it.
+      is(statusIcon.getAttribute("aria-label"), "Test Ambient 1 \u2046 (42)");
 
       ambience.counter = 0;
       Social.provider.setAmbientNotification(ambience);
       badge = statusIcon.getAttribute("badge");
       is(badge, "", "status value is correct");
+      // If there is no counter, the aria-label should be the same as the label
+      is(statusIcon.getAttribute("aria-label"), "Test Ambient 1 \u2046");
 
       // The menu bar isn't as easy to instrument on Mac.
       if (navigator.platform.contains("Mac"))
         next();
 
       // Test that keyboard accessible menuitem was added.
       let toolsPopup = document.getElementById("menu_ToolsPopup");
       toolsPopup.addEventListener("popupshown", function ontoolspopupshownAmbient() {
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -925,18 +925,24 @@ DownloadsPlacesView.prototype = {
           newOrUpdatedShell = shell;
           this._viewItemsForDataItems.set(aDataItem, shell);
           break;
         }
       }
     }
 
     if (shouldCreateShell) {
-      let shell = new DownloadElementShell(aDataItem, aPlacesNode,
-                                           this._getAnnotationsFor(downloadURI));
+      // Bug 836271: The annotations for a url should be cached only when the
+      // places node is available, i.e. when we know we we'd be notified for
+      // annoation changes. 
+      // Otherwise we may cache NOT_AVILABLE values first for a given session
+      // download, and later use these NOT_AVILABLE values when a history
+      // download for the same URL is added.
+      let cachedAnnotations = aPlacesNode ? this._getAnnotationsFor(downloadURI) : null;
+      let shell = new DownloadElementShell(aDataItem, aPlacesNode, cachedAnnotations);
       newOrUpdatedShell = shell;
       shellsForURI.add(shell);
       if (aDataItem)
         this._viewItemsForDataItems.set(aDataItem, shell);
     }
     else if (aPlacesNode) {
       for (let shell of shellsForURI) {
         if (shell.placesNode != aPlacesNode)
@@ -1168,52 +1174,73 @@ DownloadsPlacesView.prototype = {
   function DPV_invalidateContainer(aContainer) {
     if (aContainer != this._resultNode)
       throw new Error("Unexpected container node");
     if (!aContainer.containerOpen)
       throw new Error("Root container for the downloads query cannot be closed");
 
     let suppressOnSelect = this._richlistbox.suppressOnSelect;
     this._richlistbox.suppressOnSelect = true;
-
-    // Remove the invalidated history downloads from the list and unset the
-    // places node for data downloads.
-    // Loop backwards since _removeHistoryDownloadFromView may removeChild().
-    for (let i = this._richlistbox.childNodes.length - 1; i >= 0; --i) {
-      let element = this._richlistbox.childNodes[i];
-      if (element._shell.placesNode)
-        this._removeHistoryDownloadFromView(element._shell.placesNode);
+    try {
+      // Remove the invalidated history downloads from the list and unset the
+      // places node for data downloads.
+      // Loop backwards since _removeHistoryDownloadFromView may removeChild().
+      for (let i = this._richlistbox.childNodes.length - 1; i >= 0; --i) {
+        let element = this._richlistbox.childNodes[i];
+        if (element._shell.placesNode)
+          this._removeHistoryDownloadFromView(element._shell.placesNode);
+      }
+    }
+    finally {
+      this._richlistbox.suppressOnSelect = suppressOnSelect;
     }
 
-    let elementsToAppendFragment = document.createDocumentFragment();
-    for (let i = 0; i < aContainer.childCount; i++) {
-      try {
-        this._addDownloadData(null, aContainer.getChild(i), false,
-                              elementsToAppendFragment);
+    if (aContainer.childCount > 0) {
+      let elementsToAppendFragment = document.createDocumentFragment();
+      for (let i = 0; i < aContainer.childCount; i++) {
+        try {
+          this._addDownloadData(null, aContainer.getChild(i), false,
+                                elementsToAppendFragment);
+        }
+        catch(ex) {
+          Cu.reportError(ex);
+        }
       }
-      catch(ex) {
-        Cu.reportError(ex);
+
+      // _addDownloadData may not add new elements if there were already
+      // data items in place.
+      if (elementsToAppendFragment.firstChild) {
+        this._appendDownloadsFragment(elementsToAppendFragment);
+        this._ensureVisibleElementsAreActive();
       }
     }
 
-    this._appendDownloadsFragment(elementsToAppendFragment);
-    this._ensureVisibleElementsAreActive();
-
-    this._richlistbox.suppressOnSelect = suppressOnSelect;
     goUpdateDownloadCommands();
   },
 
   _appendDownloadsFragment: function DPV__appendDownloadsFragment(aDOMFragment) {
     // Workaround multiple reflows hang by removing the richlistbox
     // and adding it back when we're done.
+
+    // Hack for bug 836283: reset xbl fields to their old values after the
+    // binding is reattached to avoid breaking the selection state
+    let xblFields = new Map();
+    for (let [key, value] in Iterator(this._richlistbox)) {
+      xblFields.set(key, value);
+    }
+
     let parentNode = this._richlistbox.parentNode;
     let nextSibling = this._richlistbox.nextSibling;
     parentNode.removeChild(this._richlistbox);
     this._richlistbox.appendChild(aDOMFragment);
     parentNode.insertBefore(this._richlistbox, nextSibling);
+
+    for (let [key, value] of xblFields) {
+      this._richlistbox[key] = value;
+    }
   },
 
   nodeInserted: function DPV_nodeInserted(aParent, aPlacesNode) {
     this._addDownloadData(null, aPlacesNode);
   },
 
   nodeRemoved: function DPV_nodeRemoved(aParent, aPlacesNode, aOldIndex) {
     this._removeHistoryDownloadFromView(aPlacesNode);
--- a/browser/components/places/src/PlacesUIUtils.jsm
+++ b/browser/components/places/src/PlacesUIUtils.jsm
@@ -329,40 +329,36 @@ this.PlacesUIUtils = {
   /**
    * Shows the bookmark dialog corresponding to the specified info.
    *
    * @param aInfo
    *        Describes the item to be edited/added in the dialog.
    *        See documentation at the top of bookmarkProperties.js
    * @param aWindow
    *        Owner window for the new dialog.
-   * @param aResizable [optional]
-   *        Whether the dialog is allowed to resize.  Do not pass this for new
-   *        callers since it's deprecated.  It'll be removed in future releases.
    *
    * @see documentation at the top of bookmarkProperties.js
    * @return true if any transaction has been performed, false otherwise.
    */
   showBookmarkDialog:
-  function PUIU_showBookmarkDialog(aInfo, aParentWindow, aResizable) {
+  function PUIU_showBookmarkDialog(aInfo, aParentWindow) {
     // Preserve size attributes differently based on the fact the dialog has
     // a folder picker or not.  If the picker is visible, the dialog should
     // be resizable since it may not show enough content for the folders
     // hierarchy.
     let hasFolderPicker = !("hiddenRows" in aInfo) ||
                           aInfo.hiddenRows.indexOf("folderPicker") == -1;
-    let resizable = aResizable !== undefined ? aResizable : hasFolderPicker;
     // Use a different chrome url, since this allows to persist different sizes,
     // based on resizability of the dialog.
-    let dialogURL = resizable ?
+    let dialogURL = hasFolderPicker ?
                     "chrome://browser/content/places/bookmarkProperties2.xul" :
                     "chrome://browser/content/places/bookmarkProperties.xul";
 
     let features =
-      "centerscreen,chrome,modal,resizable=" + (resizable ? "yes" : "no");
+      "centerscreen,chrome,modal,resizable=" + (hasFolderPicker ? "yes" : "no");
 
     aParentWindow.openDialog(dialogURL, "",  features, aInfo);
     return ("performed" in aInfo && aInfo.performed);
   },
 
   _getTopBrowserWin: function PUIU__getTopBrowserWin() {
     return Services.wm.getMostRecentWindow("navigator:browser");
   },
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -394,16 +394,19 @@ social.turnOn.accesskey=T
 social.error.message=%1$S is unable to connect with %2$S right now.
 social.error.tryAgain.label=Try Again
 social.error.tryAgain.accesskey=T
 social.error.ok.label=OK
 social.error.ok.accesskey=O
 social.error.closeSidebar.label=Close This Sidebar
 social.error.closeSidebar.accesskey=C
 
+# LOCALIZATION NOTE: %1$S is the label for the toolbar button, %2$S is the associated badge numbering that the social provider may provide.
+social.aria.toolbarButtonBadgeText=%1$S (%2$S)
+
 # Identity notifications popups
 identity.termsOfService = Terms of Service
 identity.privacyPolicy = Privacy Policy
 # LOCALIZATION NOTE (identity.chooseIdentity.description): %S is the website origin (e.g. https://www.mozilla.org) shown in popup notifications.
 identity.chooseIdentity.description = Sign in to %S
 identity.chooseIdentity.label = Use an existing email
 identity.newIdentity.label = Use a different email
 identity.newIdentity.accessKey = e
--- a/browser/modules/Makefile.in
+++ b/browser/modules/Makefile.in
@@ -16,17 +16,16 @@ TEST_DIRS += test
 EXTRA_JS_MODULES = \
 	AboutHomeUtils.jsm \
 	BrowserNewTabPreloader.jsm \
 	openLocationLastURL.jsm \
 	NetworkPrioritizer.jsm \
 	NewTabUtils.jsm \
 	offlineAppCache.jsm \
 	SignInToWebsite.jsm \
-	TelemetryTimestamps.jsm \
 	webappsUI.jsm \
 	webrtcUI.jsm \
 	KeywordURLResetPrompter.jsm \
 	Social.jsm \
 	SharedFrame.jsm \
 	$(NULL)
 
 EXTRA_PP_JS_MODULES = \
--- a/browser/modules/test/Makefile.in
+++ b/browser/modules/test/Makefile.in
@@ -15,17 +15,16 @@ DIRS = \
 include $(DEPTH)/config/autoconf.mk
 
 XPCSHELL_TESTS = unit
 
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
                  browser_NetworkPrioritizer.js \
-                 browser_TelemetryTimestamps.js \
                  # bug 793906 - temporarily disabling desktop UI while working on b2g
                  # browser_SignInToWebsite.js \
                  $(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 _BROWSER_FILES += \
                  browser_taskbar_preview.js \
                  $(NULL)
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -7536,25 +7536,40 @@ nsDocument::UnblockOnload(bool aFireSync
 
   if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) {
     NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call");
     return;
   }
 
   --mOnloadBlockCount;
 
-  // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
-  // -- it's not ours.
-  if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
-    if (aFireSync && mAsyncOnloadBlockCount == 0) {
-      // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
-      ++mOnloadBlockCount;
-      DoUnblockOnload();
-    } else {
-      PostUnblockOnloadEvent();
+  if (mOnloadBlockCount == 0) {
+    if (mScriptGlobalObject) {
+      // Only manipulate the loadgroup in this case, because if mScriptGlobalObject
+      // is null, it's not ours.
+      if (aFireSync && mAsyncOnloadBlockCount == 0) {
+        // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
+        ++mOnloadBlockCount;
+        DoUnblockOnload();
+      } else {
+        PostUnblockOnloadEvent();
+      }
+    } else if (mIsBeingUsedAsImage) {
+      // To correctly unblock onload for a document that contains an SVG
+      // image, we need to know when all of the SVG document's resources are
+      // done loading, in a way comparable to |window.onload|. We fire this
+      // event to indicate that the SVG should be considered fully loaded.
+      // Because scripting is disabled on SVG-as-image documents, this event
+      // is not accessible to content authors. (See bug 837135.)
+      nsRefPtr<nsAsyncDOMEvent> e =
+        new nsAsyncDOMEvent(this,
+                            NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
+                            false,
+                            false);
+      e->PostDOMEvent();
     }
   }
 }
 
 class nsUnblockOnloadEvent : public nsRunnable {
 public:
   nsUnblockOnloadEvent(nsDocument *doc) : mDoc(doc) {}
   NS_IMETHOD Run() {
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -61,17 +61,16 @@
 #include "nsDOMDragEvent.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIDOMMozBrowserFrame.h"
 #include "nsIMozBrowserFrame.h"
 
 #include "nsCaret.h"
 
 #include "nsSubDocumentFrame.h"
-#include "nsIFrameTraversal.h"
 #include "nsLayoutCID.h"
 #include "nsLayoutUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsContentUtils.h"
 
 #include "imgIContainer.h"
 #include "nsIProperties.h"
@@ -112,18 +111,16 @@ using namespace mozilla;
 using namespace mozilla::dom;
 
 //#define DEBUG_DOCSHELL_FOCUS
 
 #define NS_USER_INTERACTION_INTERVAL 5000 // ms
 
 static const nsIntPoint kInvalidRefPoint = nsIntPoint(-1,-1);
 
-static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
-
 static bool sLeftClickOnly = true;
 static bool sKeyCausesActivation = true;
 static uint32_t sESMInstanceCount = 0;
 static int32_t sChromeAccessModifier = 0, sContentAccessModifier = 0;
 int32_t nsEventStateManager::sUserInputEventDepth = 0;
 bool nsEventStateManager::sNormalLMouseEventInProcess = false;
 nsEventStateManager* nsEventStateManager::sActiveESM = nullptr;
 nsIDocument* nsEventStateManager::sMouseOverDocument = nullptr;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1482,17 +1482,19 @@ Navigator::OnNavigation()
   nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
   if (!win) {
     return;
   }
 
 #ifdef MOZ_MEDIA_NAVIGATOR
   // Inform MediaManager in case there are live streams or pending callbacks.
   MediaManager *manager = MediaManager::Get();
-  manager->OnNavigation(win->WindowID());
+  if (manager) {
+    manager->OnNavigation(win->WindowID());
+  }
 #endif
   if (mCameraManager) {
     mCameraManager->OnNavigation(win->WindowID());
   }
 }
 
 bool
 Navigator::CheckPermission(const char* type)
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -87,17 +87,17 @@ nsContentPermissionRequestProxy::GetPrin
 NS_IMETHODIMP
 nsContentPermissionRequestProxy::GetElement(nsIDOMElement * *aRequestingElement)
 {
   NS_ENSURE_ARG_POINTER(aRequestingElement);
   if (mParent == nullptr) {
     return NS_ERROR_FAILURE;
   }
 
-  NS_ADDREF(*aRequestingElement = mParent->mElement);
+  NS_IF_ADDREF(*aRequestingElement = mParent->mElement);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsContentPermissionRequestProxy::Cancel()
 {
   if (mParent == nullptr) {
     return NS_ERROR_FAILURE;
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1056,17 +1056,17 @@ nsJSContext::JSOptionChangedCallback(con
 #endif
 
   bool werror = Preferences::GetBool(js_werror_option_str);
   if (werror)
     newDefaultJSOptions |= JSOPTION_WERROR;
   else
     newDefaultJSOptions &= ~JSOPTION_WERROR;
 
-  ::JS_SetOptions(context->mContext, newDefaultJSOptions & JSRUNOPTION_MASK);
+  ::JS_SetOptions(context->mContext, newDefaultJSOptions & JSOPTION_MASK);
 
   ::JS_SetParallelCompilationEnabled(context->mContext, parallelIonCompilation);
 
   // Save the new defaults for the next page load (InitContext).
   context->mDefaultJSOptions = newDefaultJSOptions;
 
   JSRuntime *rt = JS_GetRuntime(context->mContext);
   JS_SetJitHardening(rt, useHardening);
--- a/dom/browser-element/BrowserElementChild.js
+++ b/dom/browser-element/BrowserElementChild.js
@@ -3,863 +3,34 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu }  = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Geometry.jsm");
-Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
-
-// Event whitelisted for bubbling.
-let whitelistedEvents = [
-  Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE,   // Back button.
-  Ci.nsIDOMKeyEvent.DOM_VK_SLEEP,    // Power button.
-  Ci.nsIDOMKeyEvent.DOM_VK_CONTEXT_MENU,
-  Ci.nsIDOMKeyEvent.DOM_VK_F5,       // Search button.
-  Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP,  // Volume up.
-  Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN // Volume down.
-];
 
 function debug(msg) {
   //dump("BrowserElementChild - " + msg + "\n");
 }
 
-function sendAsyncMsg(msg, data) {
-  if (!data) {
-    data = { };
-  }
+// NB: this must happen before we process any messages from
+// mozbrowser API clients.
+docShell.isActive = true;
+
+let infos = sendSyncMessage('browser-element-api:call',
+                            { 'msg_name': 'hello' })[0];
+docShell.QueryInterface(Ci.nsIDocShellTreeItem).name = infos.name;
+docShell.setFullscreenAllowed(infos.fullscreenAllowed);
 
-  data.msg_name = msg;
-  sendAsyncMessage('browser-element-api:call', data);
-}
 
-function sendSyncMsg(msg, data) {
-  if (!data) {
-    data = { };
+if (!('BrowserElementIsPreloaded' in this)) {
+  // This is a produc-specific file that's sometimes unavailable.
+  try {
+    Services.scriptloader.loadSubScript("chrome://browser/content/forms.js");
+  } catch (e) {
   }
-
-  data.msg_name = msg;
-  return sendSyncMessage('browser-element-api:call', data);
+  Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanning.js");
+  Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js");
 }
 
-/**
- * The BrowserElementChild implements one half of <iframe mozbrowser>.
- * (The other half is, unsurprisingly, BrowserElementParent.)
- *
- * This script is injected into an <iframe mozbrowser> via
- * nsIMessageManager::LoadFrameScript().
- *
- * Our job here is to listen for events within this frame and bubble them up to
- * the parent process.
- */
-
-var global = this;
-
-function BrowserElementChild() {
-  // Maps outer window id --> weak ref to window.  Used by modal dialog code.
-  this._windowIDDict = {};
-
-  // _forcedVisible corresponds to the visibility state our owner has set on us
-  // (via iframe.setVisible).  ownerVisible corresponds to whether the docShell
-  // whose window owns this element is visible.
-  //
-  // Our docShell is visible iff _forcedVisible and _ownerVisible are both
-  // true.
-  this._forcedVisible = true;
-  this._ownerVisible = true;
-
-  this._nextPaintHandler = null;
-
-  this._init();
-};
-
-BrowserElementChild.prototype = {
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
-
-  _init: function() {
-    debug("Starting up.");
-
-    // NB: this must happen before we process any messages from
-    // mozbrowser API clients.
-    docShell.isActive = true;
-
-    sendAsyncMsg("hello");
-
-    // Set the docshell's name according to our <iframe>'s name attribute.
-    docShell.QueryInterface(Ci.nsIDocShellTreeItem).name =
-      sendSyncMsg('get-name')[0];
-
-    docShell.setFullscreenAllowed(sendSyncMsg('get-fullscreen-allowed')[0]);
-
-    BrowserElementPromptService.mapWindowToBrowserElementChild(content, this);
-
-    docShell.QueryInterface(Ci.nsIWebProgress)
-            .addProgressListener(this._progressListener,
-                                 Ci.nsIWebProgress.NOTIFY_LOCATION |
-                                 Ci.nsIWebProgress.NOTIFY_SECURITY |
-                                 Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
-
-    docShell.QueryInterface(Ci.nsIWebNavigation)
-            .sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
-                                .createInstance(Ci.nsISHistory);
-
-    // This is necessary to get security web progress notifications.
-    var securityUI = Cc['@mozilla.org/secure_browser_ui;1']
-                       .createInstance(Ci.nsISecureBrowserUI);
-    securityUI.init(content);
-
-    // A cache of the menuitem dom objects keyed by the id we generate
-    // and pass to the embedder
-    this._ctxHandlers = {};
-    // Counter of contextmenu events fired
-    this._ctxCounter = 0;
-
-    addEventListener('DOMTitleChanged',
-                     this._titleChangedHandler.bind(this),
-                     /* useCapture = */ true,
-                     /* wantsUntrusted = */ false);
-
-    addEventListener('DOMLinkAdded',
-                     this._iconChangedHandler.bind(this),
-                     /* useCapture = */ true,
-                     /* wantsUntrusted = */ false);
-
-    // Registers a MozAfterPaint handler for the very first paint.
-    this._addMozAfterPaintHandler(function () {
-      sendAsyncMsg('firstpaint');
-    });
-
-    let self = this;
-
-    let mmCalls = {
-      "purge-history": this._recvPurgeHistory,
-      "get-screenshot": this._recvGetScreenshot,
-      "set-visible": this._recvSetVisible,
-      "get-visible": this._recvVisible,
-      "send-mouse-event": this._recvSendMouseEvent,
-      "send-touch-event": this._recvSendTouchEvent,
-      "get-can-go-back": this._recvCanGoBack,
-      "get-can-go-forward": this._recvCanGoForward,
-      "go-back": this._recvGoBack,
-      "go-forward": this._recvGoForward,
-      "reload": this._recvReload,
-      "stop": this._recvStop,
-      "unblock-modal-prompt": this._recvStopWaiting,
-      "fire-ctx-callback": this._recvFireCtxCallback,
-      "owner-visibility-change": this._recvOwnerVisibilityChange,
-      "exit-fullscreen": this._recvExitFullscreen.bind(this),
-      "activate-next-paint-listener": this._activateNextPaintListener.bind(this),
-      "deactivate-next-paint-listener": this._deactivateNextPaintListener.bind(this)
-    }
-
-    addMessageListener("browser-element-api:call", function(aMessage) {
-      if (aMessage.data.msg_name in mmCalls) {
-        return mmCalls[aMessage.data.msg_name].apply(self, arguments);
-      }
-    });
-
-    let els = Cc["@mozilla.org/eventlistenerservice;1"]
-                .getService(Ci.nsIEventListenerService);
-
-    // We are using the system group for those events so if something in the
-    // content called .stopPropagation() this will still be called.
-    els.addSystemEventListener(global, 'keydown',
-                               this._keyEventHandler.bind(this),
-                               /* useCapture = */ true);
-    els.addSystemEventListener(global, 'keypress',
-                               this._keyEventHandler.bind(this),
-                               /* useCapture = */ true);
-    els.addSystemEventListener(global, 'keyup',
-                               this._keyEventHandler.bind(this),
-                               /* useCapture = */ true);
-    els.addSystemEventListener(global, 'DOMWindowClose',
-                               this._windowCloseHandler.bind(this),
-                               /* useCapture = */ false);
-    els.addSystemEventListener(global, 'DOMWindowCreated',
-                               this._windowCreatedHandler.bind(this),
-                               /* useCapture = */ true);
-    els.addSystemEventListener(global, 'contextmenu',
-                               this._contextmenuHandler.bind(this),
-                               /* useCapture = */ false);
-    els.addSystemEventListener(global, 'scroll',
-                               this._scrollEventHandler.bind(this),
-                               /* useCapture = */ false);
-
-    Services.obs.addObserver(this,
-                             "fullscreen-origin-change",
-                             /* ownsWeak = */ true);
-
-    Services.obs.addObserver(this,
-                             'ask-parent-to-exit-fullscreen',
-                             /* ownsWeak = */ true);
-
-    Services.obs.addObserver(this,
-                             'ask-parent-to-rollback-fullscreen',
-                             /* ownsWeak = */ true);
-  },
-
-  observe: function(subject, topic, data) {
-    // Ignore notifications not about our document.
-    if (subject != content.document)
-      return;
-    switch (topic) {
-      case 'fullscreen-origin-change':
-        sendAsyncMsg('fullscreen-origin-change', { _payload_: data });
-        break;
-      case 'ask-parent-to-exit-fullscreen':
-        sendAsyncMsg('exit-fullscreen');
-        break;
-      case 'ask-parent-to-rollback-fullscreen':
-        sendAsyncMsg('rollback-fullscreen');
-        break;
-    }
-  },
-
-  _tryGetInnerWindowID: function(win) {
-    let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIDOMWindowUtils);
-    try {
-      return utils.currentInnerWindowID;
-    }
-    catch(e) {
-      return null;
-    }
-  },
-
-  /**
-   * Show a modal prompt.  Called by BrowserElementPromptService.
-   */
-  showModalPrompt: function(win, args) {
-    let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIDOMWindowUtils);
-
-    args.windowID = { outer: utils.outerWindowID,
-                      inner: this._tryGetInnerWindowID(win) };
-    sendAsyncMsg('showmodalprompt', args);
-
-    let returnValue = this._waitForResult(win);
-
-    if (args.promptType == 'prompt' ||
-        args.promptType == 'confirm' ||
-        args.promptType == 'custom-prompt') {
-      return returnValue;
-    }
-  },
-
-  /**
-   * Spin in a nested event loop until we receive a unblock-modal-prompt message for
-   * this window.
-   */
-  _waitForResult: function(win) {
-    debug("_waitForResult(" + win + ")");
-    let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIDOMWindowUtils);
-
-    let outerWindowID = utils.outerWindowID;
-    let innerWindowID = this._tryGetInnerWindowID(win);
-    if (innerWindowID === null) {
-      // I have no idea what waiting for a result means when there's no inner
-      // window, so let's just bail.
-      debug("_waitForResult: No inner window. Bailing.");
-      return;
-    }
-
-    this._windowIDDict[outerWindowID] = Cu.getWeakReference(win);
-
-    debug("Entering modal state (outerWindowID=" + outerWindowID + ", " +
-                                "innerWindowID=" + innerWindowID + ")");
-
-    // In theory, we're supposed to pass |modalStateWin| back to
-    // leaveModalStateWithWindow.  But in practice, the window is always null,
-    // because it's the window associated with this script context, which
-    // doesn't have a window.  But we'll play along anyway in case this
-    // changes.
-    var modalStateWin = utils.enterModalStateWithWindow();
-
-    // We'll decrement win.modalDepth when we receive a unblock-modal-prompt message
-    // for the window.
-    if (!win.modalDepth) {
-      win.modalDepth = 0;
-    }
-    win.modalDepth++;
-    let origModalDepth = win.modalDepth;
-
-    let thread = Services.tm.currentThread;
-    debug("Nested event loop - begin");
-    while (win.modalDepth == origModalDepth) {
-      // Bail out of the loop if the inner window changed; that means the
-      // window navigated.
-      if (this._tryGetInnerWindowID(win) !== innerWindowID) {
-        debug("_waitForResult: Inner window ID changed " +
-              "while in nested event loop.");
-        break;
-      }
-
-      thread.processNextEvent(/* mayWait = */ true);
-    }
-    debug("Nested event loop - finish");
-
-    // If we exited the loop because the inner window changed, then bail on the
-    // modal prompt.
-    if (innerWindowID !== this._tryGetInnerWindowID(win)) {
-      throw Components.Exception("Modal state aborted by navigation",
-                                 Cr.NS_ERROR_NOT_AVAILABLE);
-    }
-
-    let returnValue = win.modalReturnValue;
-    delete win.modalReturnValue;
-
-    utils.leaveModalStateWithWindow(modalStateWin);
-
-    debug("Leaving modal state (outerID=" + outerWindowID + ", " +
-                               "innerID=" + innerWindowID + ")");
-    return returnValue;
-  },
-
-  _recvStopWaiting: function(msg) {
-    let outerID = msg.json.windowID.outer;
-    let innerID = msg.json.windowID.inner;
-    let returnValue = msg.json.returnValue;
-    debug("recvStopWaiting(outer=" + outerID + ", inner=" + innerID +
-          ", returnValue=" + returnValue + ")");
-
-    if (!this._windowIDDict[outerID]) {
-      debug("recvStopWaiting: No record of outer window ID " + outerID);
-      return;
-    }
-
-    let win = this._windowIDDict[outerID].get();
-    delete this._windowIDDict[outerID];
-
-    if (!win) {
-      debug("recvStopWaiting, but window is gone\n");
-      return;
-    }
-
-    if (innerID !== this._tryGetInnerWindowID(win)) {
-      debug("recvStopWaiting, but inner ID has changed\n");
-      return;
-    }
-
-    debug("recvStopWaiting " + win);
-    win.modalReturnValue = returnValue;
-    win.modalDepth--;
-  },
-
-  _recvExitFullscreen: function() {
-    var utils = content.document.defaultView
-                       .QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDOMWindowUtils);
-    utils.exitFullscreen();
-  },
-
-  _titleChangedHandler: function(e) {
-    debug("Got titlechanged: (" + e.target.title + ")");
-    var win = e.target.defaultView;
-
-    // Ignore titlechanges which don't come from the top-level
-    // <iframe mozbrowser> window.
-    if (win == content) {
-      sendAsyncMsg('titlechange', { _payload_: e.target.title });
-    }
-    else {
-      debug("Not top level!");
-    }
-  },
-
-  _iconChangedHandler: function(e) {
-    debug("Got iconchanged: (" + e.target.href + ")");
-    var hasIcon = e.target.rel.split(' ').some(function(x) {
-      return x.toLowerCase() === 'icon';
-    });
-
-    if (hasIcon) {
-      var win = e.target.ownerDocument.defaultView;
-      // Ignore iconchanges which don't come from the top-level
-      // <iframe mozbrowser> window.
-      if (win == content) {
-        sendAsyncMsg('iconchange', { _payload_: e.target.href });
-      }
-      else {
-        debug("Not top level!");
-      }
-    }
-  },
-
-  _addMozAfterPaintHandler: function(callback) {
-    function onMozAfterPaint() {
-      let uri = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
-      debug("Got afterpaint event: " + uri.spec);
-      if (uri.spec != "about:blank") {
-        removeEventListener('MozAfterPaint', onMozAfterPaint,
-                            /* useCapture = */ true);
-        callback();
-      }
-    }
-
-    addEventListener('MozAfterPaint', onMozAfterPaint, /* useCapture = */ true);
-    return onMozAfterPaint;
-  },
-
-  _removeMozAfterPaintHandler: function(listener) {
-    removeEventListener('MozAfterPaint', listener,
-                        /* useCapture = */ true);
-  },
-
-  _activateNextPaintListener: function(e) {
-    if (!this._nextPaintHandler) {
-      this._nextPaintHandler = this._addMozAfterPaintHandler(function () {
-        this._nextPaintHandler = null;
-        sendAsyncMsg('nextpaint');
-      }.bind(this));
-    }
-  },
-
-  _deactivateNextPaintListener: function(e) {
-    if (this._nextPaintHandler) {
-      this._removeMozAfterPaintHandler(this._nextPaintHandler);
-      this._nextPaintHandler = null;
-    }
-  },
-
-  _windowCloseHandler: function(e) {
-    let win = e.target;
-    if (win != content || e.defaultPrevented) {
-      return;
-    }
-
-    debug("Closing window " + win);
-    sendAsyncMsg('close');
-
-    // Inform the window implementation that we handled this close ourselves.
-    e.preventDefault();
-  },
-
-  _windowCreatedHandler: function(e) {
-    let targetDocShell = e.target.defaultView
-          .QueryInterface(Ci.nsIInterfaceRequestor)
-          .getInterface(Ci.nsIWebNavigation);
-    if (targetDocShell != docShell) {
-      return;
-    }
-
-    let uri = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
-    debug("Window created: " + uri.spec);
-    if (uri.spec != "about:blank") {
-      this._addMozAfterPaintHandler(function () {
-        sendAsyncMsg('documentfirstpaint');
-      });
-    }
-  },
-
-  _contextmenuHandler: function(e) {
-    debug("Got contextmenu");
-
-    if (e.defaultPrevented) {
-      return;
-    }
-
-    e.preventDefault();
-
-    this._ctxCounter++;
-    this._ctxHandlers = {};
-
-    var elem = e.target;
-    var menuData = {systemTargets: [], contextmenu: null};
-    var ctxMenuId = null;
-
-    while (elem && elem.parentNode) {
-      var ctxData = this._getSystemCtxMenuData(elem);
-      if (ctxData) {
-        menuData.systemTargets.push({
-          nodeName: elem.nodeName,
-          data: ctxData
-        });
-      }
-
-      if (!ctxMenuId && 'hasAttribute' in elem && elem.hasAttribute('contextmenu')) {
-        ctxMenuId = elem.getAttribute('contextmenu');
-      }
-      elem = elem.parentNode;
-    }
-
-    if (ctxMenuId) {
-      var menu = e.target.ownerDocument.getElementById(ctxMenuId);
-      if (menu) {
-        menuData.contextmenu = this._buildMenuObj(menu, '');
-      }
-    }
-    sendAsyncMsg('contextmenu', menuData);
-  },
-
-  _getSystemCtxMenuData: function(elem) {
-    if ((elem instanceof Ci.nsIDOMHTMLAnchorElement && elem.href) ||
-        (elem instanceof Ci.nsIDOMHTMLAreaElement && elem.href)) {
-      return elem.href;
-    }
-    if (elem instanceof Ci.nsIImageLoadingContent && elem.currentURI) {
-      return elem.currentURI.spec;
-    }
-    if ((elem instanceof Ci.nsIDOMHTMLMediaElement) ||
-        (elem instanceof Ci.nsIDOMHTMLImageElement)) {
-      return elem.currentSrc || elem.src;
-    }
-    return false;
-  },
-
-  _scrollEventHandler: function(e) {
-    let win = e.target.defaultView;
-    if (win != content) {
-      return;
-    }
-
-    debug("scroll event " + win);
-    sendAsyncMsg("scroll", { top: win.scrollY, left: win.scrollX });
-  },
-
-  _recvPurgeHistory: function(data) {
-    debug("Received purgeHistory message: (" + data.json.id + ")");
-
-    let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
-
-    try {
-      if (history && history.count) {
-        history.PurgeHistory(history.count);
-      }
-    } catch(e) {}
-
-    sendAsyncMsg('got-purge-history', { id: data.json.id, successRv: true });
-  },
-
-  _recvGetScreenshot: function(data) {
-    debug("Received getScreenshot message: (" + data.json.id + ")");
-
-    let self = this;
-    let maxWidth = data.json.args.width;
-    let maxHeight = data.json.args.height;
-    let domRequestID = data.json.id;
-
-    let takeScreenshotClosure = function() {
-      self._takeScreenshot(maxWidth, maxHeight, domRequestID);
-    };
-
-    let maxDelayMS = 2000;
-    try {
-      maxDelayMS = Services.prefs.getIntPref('dom.browserElement.maxScreenshotDelayMS');
-    }
-    catch(e) {}
-
-    // 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);
-  },
-
-  /**
-   * Actually take a screenshot and foward the result up to our parent, given
-   * the desired maxWidth and maxHeight, and given the DOMRequest ID associated
-   * with the request from the parent.
-   */
-  _takeScreenshot: function(maxWidth, maxHeight, domRequestID) {
-    // You can think of the screenshotting algorithm as carrying out the
-    // following steps:
-    //
-    // - Let scaleWidth be the factor by which we'd need to downscale the
-    //   viewport so it would fit within maxWidth.  (If the viewport's width
-    //   is less than maxWidth, let scaleWidth be 1.) Compute scaleHeight
-    //   the same way.
-    //
-    // - Scale the viewport by max(scaleWidth, scaleHeight).  Now either the
-    //   viewport's width is no larger than maxWidth, the viewport's height is
-    //   no larger than maxHeight, or both.
-    //
-    // - Crop the viewport so its width is no larger than maxWidth and its
-    //   height is no larger than maxHeight.
-    //
-    // - Return a screenshot of the page's viewport scaled and cropped per
-    //   above.
-    debug("Taking a screenshot: maxWidth=" + maxWidth +
-          ", maxHeight=" + maxHeight +
-          ", domRequestID=" + domRequestID + ".");
-
-    let scaleWidth = Math.min(1, maxWidth / content.innerWidth);
-    let scaleHeight = Math.min(1, maxHeight / content.innerHeight);
-
-    let scale = Math.max(scaleWidth, scaleHeight);
-
-    let canvasWidth = Math.min(maxWidth, Math.round(content.innerWidth * scale));
-    let canvasHeight = Math.min(maxHeight, Math.round(content.innerHeight * scale));
-
-    var canvas = content.document
-      .createElementNS("http://www.w3.org/1999/xhtml", "canvas");
-    canvas.mozOpaque = true;
-    canvas.width = canvasWidth;
-    canvas.height = canvasHeight;
-
-    var ctx = canvas.getContext("2d");
-    ctx.scale(scale, scale);
-    ctx.drawWindow(content, 0, 0, content.innerWidth, content.innerHeight,
-                   "rgb(255,255,255)");
-
-    // Take a JPEG screenshot to hack around the fact that we can't specify
-    // opaque PNG.  This requires us to unpremultiply the alpha channel, which
-    // is expensive on ARM processors because they lack a hardware integer
-    // division instruction.
-    canvas.toBlob(function(blob) {
-      sendAsyncMsg('got-screenshot', {
-        id: domRequestID,
-        successRv: blob
-      });
-    }, 'image/jpeg');
-  },
-
-  _recvFireCtxCallback: function(data) {
-    debug("Received fireCtxCallback message: (" + data.json.menuitem + ")");
-    // We silently ignore if the embedder uses an incorrect id in the callback
-    if (data.json.menuitem in this._ctxHandlers) {
-      this._ctxHandlers[data.json.menuitem].click();
-      this._ctxHandlers = {};
-    } else {
-      debug("Ignored invalid contextmenu invocation");
-    }
-  },
-
-  _buildMenuObj: function(menu, idPrefix) {
-    function maybeCopyAttribute(src, target, attribute) {
-      if (src.getAttribute(attribute)) {
-        target[attribute] = src.getAttribute(attribute);
-      }
-    }
-
-    var menuObj = {type: 'menu', items: []};
-    maybeCopyAttribute(menu, menuObj, 'label');
-
-    for (var i = 0, child; child = menu.children[i++];) {
-      if (child.nodeName === 'MENU') {
-        menuObj.items.push(this._buildMenuObj(child, idPrefix + i + '_'));
-      } else if (child.nodeName === 'MENUITEM') {
-        var id = this._ctxCounter + '_' + idPrefix + i;
-        var menuitem = {id: id, type: 'menuitem'};
-        maybeCopyAttribute(child, menuitem, 'label');
-        maybeCopyAttribute(child, menuitem, 'icon');
-        this._ctxHandlers[id] = child;
-        menuObj.items.push(menuitem);
-      }
-    }
-    return menuObj;
-  },
-
-  _recvSetVisible: function(data) {
-    debug("Received setVisible message: (" + data.json.visible + ")");
-    this._forcedVisible = data.json.visible;
-    this._updateDocShellVisibility();
-  },
-
-  _recvVisible: function(data) {
-    sendAsyncMsg('got-visible', {
-      id: data.json.id,
-      successRv: docShell.isActive
-    });
-  },
-
-  /**
-   * Called when the window which contains this iframe becomes hidden or
-   * visible.
-   */
-  _recvOwnerVisibilityChange: function(data) {
-    debug("Received ownerVisibilityChange: (" + data.json.visible + ")");
-    this._ownerVisible = data.json.visible;
-    this._updateDocShellVisibility();
-  },
-
-  _updateDocShellVisibility: function() {
-    var visible = this._forcedVisible && this._ownerVisible;
-    if (docShell.isActive !== visible) {
-      docShell.isActive = visible;
-    }
-  },
-
-  _recvSendMouseEvent: function(data) {
-    let json = data.json;
-    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDOMWindowUtils);
-    utils.sendMouseEvent(json.type, json.x, json.y, json.button,
-                         json.clickCount, json.modifiers);
-  },
-
-  _recvSendTouchEvent: function(data) {
-    let json = data.json;
-    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDOMWindowUtils);
-    utils.sendTouchEvent(json.type, json.identifiers, json.touchesX,
-                         json.touchesY, json.radiisX, json.radiisY,
-                         json.rotationAngles, json.forces, json.count,
-                         json.modifiers);
-  },
-
-  _recvCanGoBack: function(data) {
-    var webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
-    sendAsyncMsg('got-can-go-back', {
-      id: data.json.id,
-      successRv: webNav.canGoBack
-    });
-  },
-
-  _recvCanGoForward: function(data) {
-    var webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
-    sendAsyncMsg('got-can-go-forward', {
-      id: data.json.id,
-      successRv: webNav.canGoForward
-    });
-  },
-
-  _recvGoBack: function(data) {
-    try {
-      docShell.QueryInterface(Ci.nsIWebNavigation).goBack();
-    } catch(e) {
-      // Silently swallow errors; these happen when we can't go back.
-    }
-  },
-
-  _recvGoForward: function(data) {
-    try {
-      docShell.QueryInterface(Ci.nsIWebNavigation).goForward();
-    } catch(e) {
-      // Silently swallow errors; these happen when we can't go forward.
-    }
-  },
-
-  _recvReload: function(data) {
-    let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
-    let reloadFlags = data.json.hardReload ?
-      webNav.LOAD_FLAGS_BYPASS_PROXY | webNav.LOAD_FLAGS_BYPASS_CACHE :
-      webNav.LOAD_FLAGS_NONE;
-    try {
-      webNav.reload(reloadFlags);
-    } catch(e) {
-      // Silently swallow errors; these can happen if a used cancels reload
-    }
-  },
-
-  _recvStop: function(data) {
-    let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
-    webNav.stop(webNav.STOP_NETWORK);
-  },
-
-  _keyEventHandler: function(e) {
-    if (whitelistedEvents.indexOf(e.keyCode) != -1 && !e.defaultPrevented) {
-      sendAsyncMsg('keyevent', {
-        type: e.type,
-        keyCode: e.keyCode,
-        charCode: e.charCode,
-      });
-    }
-  },
-
-  // The docShell keeps a weak reference to the progress listener, so we need
-  // to keep a strong ref to it ourselves.
-  _progressListener: {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                           Ci.nsISupportsWeakReference]),
-    _seenLoadStart: false,
-
-    onLocationChange: function(webProgress, request, location, flags) {
-      // We get progress events from subshells here, which is kind of weird.
-      if (webProgress != docShell) {
-        return;
-      }
-
-      // Ignore locationchange events which occur before the first loadstart.
-      // These are usually about:blank loads we don't care about.
-      if (!this._seenLoadStart) {
-        return;
-      }
-
-      // Remove password and wyciwyg from uri.
-      location = Cc["@mozilla.org/docshell/urifixup;1"]
-        .getService(Ci.nsIURIFixup).createExposableURI(location);
-
-      sendAsyncMsg('locationchange', { _payload_: location.spec });
-    },
-
-    onStateChange: function(webProgress, request, stateFlags, status) {
-      if (webProgress != docShell) {
-        return;
-      }
-
-      if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
-        this._seenLoadStart = true;
-        sendAsyncMsg('loadstart');
-      }
-
-      if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
-        sendAsyncMsg('loadend');
-
-        // Ignoring NS_BINDING_ABORTED, which is set when loading page is
-        // stopped.
-        if (status == Cr.NS_OK ||
-            status == Cr.NS_BINDING_ABORTED) {
-          return;
-        }
-
-        // TODO See nsDocShell::DisplayLoadError for a list of all the error
-        // codes (the status param) we should eventually handle here.
-        sendAsyncMsg('error', { type: 'other' });
-      }
-    },
-
-    onSecurityChange: function(webProgress, request, state) {
-      if (webProgress != docShell) {
-        return;
-      }
-
-      var stateDesc;
-      if (state & Ci.nsIWebProgressListener.STATE_IS_SECURE) {
-        stateDesc = 'secure';
-      }
-      else if (state & Ci.nsIWebProgressListener.STATE_IS_BROKEN) {
-        stateDesc = 'broken';
-      }
-      else if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE) {
-        stateDesc = 'insecure';
-      }
-      else {
-        debug("Unexpected securitychange state!");
-        stateDesc = '???';
-      }
-
-      // XXX Until bug 764496 is fixed, this will always return false.
-      var isEV = !!(state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL);
-
-      sendAsyncMsg('securitychange', { state: stateDesc, extendedValidation: isEV });
-    },
-
-    onStatusChange: function(webProgress, request, status, message) {},
-    onProgressChange: function(webProgress, request, curSelfProgress,
-                               maxSelfProgress, curTotalProgress, maxTotalProgress) {},
-  },
-
-  // Expose the message manager for WebApps and others.
-  _messageManagerPublic: {
-    sendAsyncMessage: global.sendAsyncMessage.bind(global),
-    sendSyncMessage: global.sendSyncMessage.bind(global),
-    addMessageListener: global.addMessageListener.bind(global),
-    removeMessageListener: global.removeMessageListener.bind(global)
-  },
-
-  get messageManager() {
-    return this._messageManagerPublic;
-  }
-};
-
-var api = new BrowserElementChild();
-
-// FIXME/bug 775438: use a JSM?
-//
-// The code in this included file depends on the |addEventListener|,
-// |addMessageListener|, |content|, |Geometry| and |Services| symbols
-// being "exported" from here.
-#include BrowserElementScrolling.js
+var BrowserElementIsReady = true;
copy from dom/browser-element/BrowserElementChild.js
copy to dom/browser-element/BrowserElementChildPreload.js
--- a/dom/browser-element/BrowserElementChild.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -1,52 +1,51 @@
 /* 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/. */
 
 "use strict";
 
+dump("######################## BrowserElementChildPreload.js loaded\n");
+
+var BrowserElementIsReady = false;
+
 let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu }  = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Geometry.jsm");
 Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
 
 // Event whitelisted for bubbling.
 let whitelistedEvents = [
   Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE,   // Back button.
   Ci.nsIDOMKeyEvent.DOM_VK_SLEEP,    // Power button.
   Ci.nsIDOMKeyEvent.DOM_VK_CONTEXT_MENU,
   Ci.nsIDOMKeyEvent.DOM_VK_F5,       // Search button.
   Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP,  // Volume up.
   Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN // Volume down.
 ];
 
 function debug(msg) {
-  //dump("BrowserElementChild - " + msg + "\n");
+  //dump("BrowserElementChildPreload - " + msg + "\n");
 }
 
 function sendAsyncMsg(msg, data) {
+  // Ensure that we don't send any messages before BrowserElementChild.js
+  // finishes loading.
+  if (!BrowserElementIsReady)
+    return;
+
   if (!data) {
     data = { };
   }
 
   data.msg_name = msg;
   sendAsyncMessage('browser-element-api:call', data);
 }
 
-function sendSyncMsg(msg, data) {
-  if (!data) {
-    data = { };
-  }
-
-  data.msg_name = msg;
-  return sendSyncMessage('browser-element-api:call', data);
-}
-
 /**
  * The BrowserElementChild implements one half of <iframe mozbrowser>.
  * (The other half is, unsurprisingly, BrowserElementParent.)
  *
  * This script is injected into an <iframe mozbrowser> via
  * nsIMessageManager::LoadFrameScript().
  *
  * Our job here is to listen for events within this frame and bubble them up to
@@ -76,28 +75,16 @@ function BrowserElementChild() {
 BrowserElementChild.prototype = {
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
   _init: function() {
     debug("Starting up.");
 
-    // NB: this must happen before we process any messages from
-    // mozbrowser API clients.
-    docShell.isActive = true;
-
-    sendAsyncMsg("hello");
-
-    // Set the docshell's name according to our <iframe>'s name attribute.
-    docShell.QueryInterface(Ci.nsIDocShellTreeItem).name =
-      sendSyncMsg('get-name')[0];
-
-    docShell.setFullscreenAllowed(sendSyncMsg('get-fullscreen-allowed')[0]);
-
     BrowserElementPromptService.mapWindowToBrowserElementChild(content, this);
 
     docShell.QueryInterface(Ci.nsIWebProgress)
             .addProgressListener(this._progressListener,
                                  Ci.nsIWebProgress.NOTIFY_LOCATION |
                                  Ci.nsIWebProgress.NOTIFY_SECURITY |
                                  Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
 
@@ -386,18 +373,18 @@ BrowserElementChild.prototype = {
         debug("Not top level!");
       }
     }
   },
 
   _addMozAfterPaintHandler: function(callback) {
     function onMozAfterPaint() {
       let uri = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
-      debug("Got afterpaint event: " + uri.spec);
       if (uri.spec != "about:blank") {
+        debug("Got afterpaint event: " + uri.spec);
         removeEventListener('MozAfterPaint', onMozAfterPaint,
                             /* useCapture = */ true);
         callback();
       }
     }
 
     addEventListener('MozAfterPaint', onMozAfterPaint, /* useCapture = */ true);
     return onMozAfterPaint;
@@ -852,14 +839,8 @@ BrowserElementChild.prototype = {
 
   get messageManager() {
     return this._messageManagerPublic;
   }
 };
 
 var api = new BrowserElementChild();
 
-// FIXME/bug 775438: use a JSM?
-//
-// The code in this included file depends on the |addEventListener|,
-// |addMessageListener|, |content|, |Geometry| and |Services| symbols
-// being "exported" from here.
-#include BrowserElementScrolling.js
rename from dom/browser-element/BrowserElementScrolling.js
rename to dom/browser-element/BrowserElementPanning.js
--- a/dom/browser-element/BrowserElementScrolling.js
+++ b/dom/browser-element/BrowserElementPanning.js
@@ -1,15 +1,23 @@
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 sts=2 et: */
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+"use strict";
+dump("############################### browserElementPanning.js loaded\n");
+
+let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu }  = Components;
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Geometry.jsm");
+
 const ContentPanning = {
   // Are we listening to touch or mouse events?
   watchedEventsType: '',
 
   // Are mouse events being delivered to this content along with touch
   // events, in violation of spec?
   hybridEvents: false,
 
@@ -30,18 +38,18 @@ const ContentPanning = {
       this.hybridEvents = isParentProcess;
 #endif
     } catch(e) {
       // Touch events aren't supported, so fall back on mouse.
       events = ['mousedown', 'mouseup', 'mousemove'];
       this.watchedEventsType = 'mouse';
     }
     events.forEach(function(type) {
-      addEventListener(type, ContentPanning, false);
-    });
+      addEventListener(type, this, false);
+    }.bind(this));
 
     addMessageListener("Viewport:Change", this._recvViewportChange.bind(this));
     addMessageListener("Gesture:DoubleTap", this._recvDoubleTap.bind(this));
   },
 
   handleEvent: function cp_handleEvent(evt) {
     if (evt.defaultPrevented)
       return;
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -90,18 +90,16 @@ function BrowserElementParent(frameLoade
   this._mm = frameLoader.messageManager;
   let self = this;
 
   // Messages we receive are handed to functions which take a (data) argument,
   // where |data| is the message manager's data object.
   // We use a single message and dispatch to various function based
   // on data.msg_name
   let mmCalls = {
-    "get-name": this._recvGetName,
-    "get-fullscreen-allowed": this._recvGetFullscreenAllowed,
     "hello": this._recvHello,
     "contextmenu": this._fireCtxMenuEvent,
     "locationchange": this._fireEventFromMsg,
     "loadstart": this._fireEventFromMsg,
     "loadend": this._fireEventFromMsg,
     "titlechange": this._fireEventFromMsg,
     "iconchange": this._fireEventFromMsg,
     "close": this._fireEventFromMsg,
@@ -276,25 +274,23 @@ BrowserElementParent.prototype = {
 
     // Inform our child if our owner element's document is invisible.  Note
     // that we must do so here, rather than in the BrowserElementParent
     // constructor, because the BrowserElementChild may not be initialized when
     // we run our constructor.
     if (this._window.document.hidden) {
       this._ownerVisibilityChange();
     }
-  },
 
-  _recvGetName: function(data) {
-    return this._frameElement.getAttribute('name');
-  },
-
-  _recvGetFullscreenAllowed: function(data) {
-    return this._frameElement.hasAttribute('allowfullscreen') ||
-           this._frameElement.hasAttribute('mozallowfullscreen');
+    return {
+      name: this._frameElement.getAttribute('name'),
+      fullscreenAllowed:
+        this._frameElement.hasAttribute('allowfullscreen') ||
+        this._frameElement.hasAttribute('mozallowfullscreen')
+    }
   },
 
   _fireCtxMenuEvent: function(data) {
     let detail = data.json;
     let evtName = detail.msg_name;
 
     debug('fireCtxMenuEventFromMsg: ' + evtName + ' ' + detail);
     let evt = this._createEvent(evtName, detail);
@@ -316,17 +312,17 @@ BrowserElementParent.prototype = {
    * |data|.
    */
   _fireEventFromMsg: function(data) {
     let detail = data.json;
     let name = detail.msg_name;
 
     // For events that send a "_payload_" property, we just want to transmit
     // this in the event.
-    if (detail._payload_) {
+    if ("_payload_" in detail) {
       detail = detail._payload_;
     }
 
     debug('fireEventFromMsg: ' + name + ', ' + JSON.stringify(detail));
     let evt = this._createEvent(name, detail,
                                 /* cancelable = */ false);
     this._frameElement.dispatchEvent(evt);
   },
@@ -566,17 +562,17 @@ BrowserElementParent.prototype = {
                        {visible: !this._window.document.hidden});
   },
 
   _exitFullscreen: function() {
     this._windowUtils.exitFullscreen();
   },
 
   _remoteFullscreenOriginChange: function(data) {
-    let origin = data.json;
+    let origin = data.json._payload_;
     this._windowUtils.remoteFrameFullscreenChanged(this._frameElement, origin);
   },
 
   _remoteFrameFullscreenReverted: function(data) {
     this._windowUtils.remoteFrameFullscreenReverted();
   },
 
   _fireFatalError: function() {
--- a/dom/imptests/failures/editing/conformancetest/test_runtest.html.json
+++ b/dom/imptests/failures/editing/conformancetest/test_runtest.html.json
@@ -11045,18 +11045,16 @@
   "[[\"stylewithcss\",\"true\"],[\"removeformat\",\"\"]] \"foo<b id=foo>b[a]r</b>baz\" compare innerHTML":true,
   "[[\"stylewithcss\",\"false\"],[\"removeformat\",\"\"]] \"foo<b id=foo>b[a]r</b>baz\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"[foo<blink>bar</blink>baz]\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"foo<blink>b[a]r</blink>baz\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"[foo<del>bar</del>baz]\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"foo<del>b[a]r</del>baz\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"[foo<nobr>bar</nobr>baz]\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"foo<nobr>b[a]r</nobr>baz\" compare innerHTML":true,
-  "[[\"removeformat\",\"\"]] \"[foo<video></video>bar]\" compare innerHTML":true,
-  "[[\"removeformat\",\"\"]] \"[foo<video src=abc></video>bar]\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"[foo<svg><circle fill=blue r=20 cx=20 cy=20 /></svg>bar]\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"[foo<nonexistentelement>bar</nonexistentelement>baz]\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"foo<nonexistentelement>b[a]r</nonexistentelement>baz\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"[foo<nonexistentelement style=\\\"display: block\\\">bar</nonexistentelement>baz]\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"foo<nonexistentelement style=\\\"display: block\\\">b[a]r</nonexistentelement>baz\" compare innerHTML":true,
   "[[\"removeformat\",\"\"]] \"foo<span id=foo>b[a]r</span>baz\" compare innerHTML":true,
   "[[\"stylewithcss\",\"true\"],[\"removeformat\",\"\"]] \"<p style=\\\"font-weight: bold\\\">foo[bar]baz</p>\" compare innerHTML":true,
   "[[\"stylewithcss\",\"false\"],[\"removeformat\",\"\"]] \"<p style=\\\"font-weight: bold\\\">foo[bar]baz</p>\" compare innerHTML":true,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -224,18 +224,16 @@ TabChild::PreloadSlowThings()
 
     nsRefPtr<TabChild> tab(new TabChild(TabContext(), /* chromeFlags */ 0));
     if (!NS_SUCCEEDED(tab->Init()) ||
         !tab->InitTabChildGlobal(DONT_LOAD_SCRIPTS)) {
         return;
     }
     // Just load and compile these scripts, but don't run them.
     tab->TryCacheLoadAndCompileScript(BROWSER_ELEMENT_CHILD_SCRIPT);
-    tab->TryCacheLoadAndCompileScript(
-        NS_LITERAL_STRING("chrome://browser/content/forms.js"));
     // Load, compile, and run these scripts.
     tab->RecvLoadRemoteScript(
         NS_LITERAL_STRING("chrome://global/content/preload.js"));
 
     nsCOMPtr<nsIDocShell> docShell = do_GetInterface(tab->mWebNav);
     if (nsIPresShell* presShell = docShell->GetPresShell()) {
         // Initialize and do an initial reflow of the about:blank
         // PresShell to let it preload some things for us.
--- a/dom/ipc/jar.mn
+++ b/dom/ipc/jar.mn
@@ -1,9 +1,11 @@
 # 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/.
 
 toolkit.jar:
         content/global/test-ipc.xul (test.xul)
         content/global/remote-test-ipc.js (remote-test.js)
-*       content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js)
+        content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js)
+*       content/global/BrowserElementChildPreload.js (../browser-element/BrowserElementChildPreload.js)
+*       content/global/BrowserElementPanning.js (../browser-element/BrowserElementPanning.js)
         content/global/preload.js (preload.js)
--- a/dom/ipc/preload.js
+++ b/dom/ipc/preload.js
@@ -2,19 +2,21 @@
  * 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/. */
 
 // Preload some things, in an attempt to make app startup faster.
 //
 // This script is run when the preallocated process starts.  It is injected as
 // a frame script.
 
-"use strict";
+const BrowserElementIsPreloaded = true;
 
-(function () {
+(function (global) {
+  "use strict";
+
   let Cu = Components.utils;
   let Cc = Components.classes;
   let Ci = Components.interfaces;
 
   Cu.import("resource://gre/modules/AppsServiceChild.jsm");
   Cu.import("resource://gre/modules/AppsUtils.jsm");
   Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
   Cu.import("resource://gre/modules/CSPUtils.jsm");
@@ -68,15 +70,28 @@
   Cc["@mozilla.org/permissionmanager;1"].getService(Ci["nsIPermissionManager"]);
   Cc["@mozilla.org/preferences-service;1"].getService(Ci["nsIPrefBranch"]);
   Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci["nsIScriptSecurityManager"]);
   Cc["@mozilla.org/storage/service;1"].getService(Ci["mozIStorageService"]);
   Cc["@mozilla.org/system-info;1"].getService(Ci["nsIPropertyBag2"]);
   Cc["@mozilla.org/thread-manager;1"].getService(Ci["nsIThreadManager"]);
   Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci["nsIAppStartup"]);
   Cc["@mozilla.org/uriloader;1"].getService(Ci["nsIURILoader"]);
+  Cc["@mozilla.org/contentsecuritypolicy;1"].createInstance(Ci["nsIContentSecurityPolicy"]);
+
+  /* Applications Specific Helper */
+  Cc["@mozilla.org/settingsManager;1"].getService(Ci["nsIDOMSettingsManager"]);
+
+  // This is a produc-specific file that's sometimes unavailable.
+  try {
+    Services.scriptloader.loadSubScript("chrome://browser/content/forms.js", global);
+  } catch (e) {
+  }
+  Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanning.js", global);
+  Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js", global);
 
   Services.io.getProtocolHandler("app");
+  Services.io.getProtocolHandler("default");
 
   docShell.isActive = false;
   docShell.createAboutBlankContentViewer(null);
 
-})();
+})(this);
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -355,20 +355,23 @@ public:
     if (!sSingleton) {
       sSingleton = new MediaManager();
 
       NS_NewThread(getter_AddRefs(sSingleton->mMediaThread));
       MM_LOG(("New Media thread for gum"));
 
       NS_ASSERTION(NS_IsMainThread(), "Only create MediaManager on main thread");
       nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-      obs->AddObserver(sSingleton, "xpcom-shutdown", false);
-      obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
-      obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
-      obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
+      if (obs) {
+        obs->AddObserver(sSingleton, "xpcom-shutdown", false);
+        obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
+        obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
+        obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
+      }
+      // else MediaManager won't work properly and will leak (see bug 837874)
     }
     return sSingleton;
   }
   static nsIThread* GetThread() {
     return Get()->mMediaThread;
   }
 
   NS_DECL_ISUPPORTS
--- a/dom/phonenumberutils/PhoneNumber.jsm
+++ b/dom/phonenumberutils/PhoneNumber.jsm
@@ -105,24 +105,24 @@ this.PhoneNumber = (function (dataBase) 
       // an object), and their country code should have been in the cache.
       if (Array.isArray(entry)) {
         for (var n = 0; n < entry.length; n++) {
           if (typeof entry[n] == "string" && entry[n].substr(2,2) == region) {
             if (n > 0) {
               // Only the first entry has the formats field set.
               // Parse the main country if we haven't already and use
               // the formats field from the main country.
-              if (typeof entry[0] == "string" && entry[0].substr(2,2) == region)
+              if (typeof entry[0] == "string")
                 entry[0] = ParseMetaData(countryCode, entry[0]);
               let formats = entry[0].formats;
               let current = ParseMetaData(countryCode, entry[n]);
               current.formats = formats;
               return entry[n] = current;
             }
-            
+
             entry[n] = ParseMetaData(countryCode, entry[n]);
             return entry[n];
           }
         }
         continue;
       }
       if (typeof entry == "string" && entry.substr(2,2) == region)
         return dataBase[countryCode] = ParseMetaData(countryCode, entry);
--- a/dom/phonenumberutils/tests/test_phonenumber.xul
+++ b/dom/phonenumberutils/tests/test_phonenumber.xul
@@ -72,16 +72,37 @@ function Format(dial, currentRegion, nat
     return result;
   }
 }
 
 // Test parsing national numbers.
 Parse("033316005", "NZ");
 Parse("03-331 6005", "NZ");
 Parse("03 331 6005", "NZ");
+
+// Always test CA before US because CA has to load all meta-info for US.
+ParseWithIntl("4031234567", "CA");
+Parse("(416) 585-4319", "CA");
+Parse("647-967-4357", "CA");
+Parse("416-716-8768", "CA");
+Parse("18002684646", "CA");
+Parse("416-445-9119", "CA");
+Parse("1-800-668-6866", "CA");
+Parse("(416) 453-6486", "CA");
+Parse("(647) 268-4778", "CA");
+Parse("647-218-1313", "CA");
+Parse("+1 647-209-4642", "CA");
+Parse("416-559-0133", "CA");
+Parse("+1 647-639-4118", "CA");
+Parse("+12898803664", "CA");
+Parse("780-901-4687", "CA");
+Parse("+14167070550", "CA");
+Parse("+1-647-522-6487", "CA");
+Parse("(416) 877-0880", "CA");
+
 // Testing international prefixes.
 // Should strip country code.
 Parse("0064 3 331 6005", "NZ");
 // Try again, but this time we have an international number with region rode US. It should
 // recognize the country code and parse accordingly.
 Parse("01164 3 331 6005", "US");
 Parse("+64 3 331 6005", "US");
 Parse("64(0)64123456", "NZ");
@@ -130,36 +151,16 @@ Parse("023 1234 0000", "AR");
 // Test numbers in Mexico
 Parse("+52 (449)978-0001", "MX");
 Parse("01 (449)978-0001", "MX");
 Parse("(449)978-0001", "MX");
 Parse("+52 1 33 1234-5678", "MX");
 Parse("044 (33) 1234-5678", "MX");
 Parse("045 33 1234-5678", "MX");
 
-
-ParseWithIntl("4031234567", "CA");
-Parse("(416) 585-4319", "CA");
-Parse("647-967-4357", "CA");
-Parse("416-716-8768", "CA");
-Parse("18002684646", "CA");
-Parse("416-445-9119", "CA");
-Parse("1-800-668-6866", "CA");
-Parse("(416) 453-6486", "CA");
-Parse("(647) 268-4778", "CA");
-Parse("647-218-1313", "CA");
-Parse("+1 647-209-4642", "CA");
-Parse("416-559-0133", "CA");
-Parse("+1 647-639-4118", "CA");
-Parse("+12898803664", "CA");
-Parse("780-901-4687", "CA");
-Parse("+14167070550", "CA");
-Parse("+1-647-522-6487", "CA");
-Parse("(416) 877-0880", "CA");
-
 // Test that lots of spaces are ok.
 Parse("0 3   3 3 1   6 0 0 5", "NZ");
 
 // Test omitting the current region. This is only valid when the number starts
 // with a '+'.
 Parse("+64 3 331 6005");
 Parse("+64 3 331 6005", null);
 
--- a/dom/phonenumberutils/tests/test_phonenumberutils.xul
+++ b/dom/phonenumberutils/tests/test_phonenumberutils.xul
@@ -20,21 +20,32 @@
 
 "use strict";
 
 Components.utils.import("resource://gre/modules/PhoneNumberUtils.jsm");
 
 function CantParseWithMcc(dial, mcc) {
   var result = PhoneNumberUtils.parseWithMCC(dial, mcc);
   if (result) {
-  	ok(false, "Shouldn't parse!\n");
-    print("expected: does not parse");
-    print("got: " + dial + " " + mcc);
+    ok(false, "Shouldn't parse!\n");
+    dump("expected: does not parse");
+    dump("got: " + dial + " " + mcc);
   } else {
     ok(true, "Parses");
   }
 }
 
+function ParseWithMcc(dial, mcc) {
+  var result = PhoneNumberUtils.parseWithMCC(dial, mcc);
+  if (result) {
+    ok(true, "Parses!\n");
+  } else {
+    ok(false, "Should Parse");
+    dump("expected: parses");
+  }
+}
+
 // Unknown mcc
 CantParseWithMcc("1234", 123);
+ParseWithMcc("4168293997", 302);
 
 </script>
 </window>
--- a/gfx/2d/HelpersSkia.h
+++ b/gfx/2d/HelpersSkia.h
@@ -150,20 +150,49 @@ GfxOpToSkiaOp(CompositionOp op)
     case OP_DEST_OUT:
       return SkXfermode::kDstOut_Mode;
     case OP_DEST_OVER:
       return SkXfermode::kDstOver_Mode;
     case OP_DEST_ATOP:
       return SkXfermode::kDstATop_Mode;
     case OP_XOR:
       return SkXfermode::kXor_Mode;
-    case OP_COUNT:
+    case OP_MULTIPLY:
+      return SkXfermode::kMultiply_Mode;
+    case OP_SCREEN:
+      return SkXfermode::kScreen_Mode;
+    case OP_OVERLAY:
+      return SkXfermode::kOverlay_Mode;
+    case OP_DARKEN:
+      return SkXfermode::kDarken_Mode;
+    case OP_LIGHTEN:
+      return SkXfermode::kLighten_Mode;
+    case OP_COLOR_DODGE:
+      return SkXfermode::kColorDodge_Mode;
+    case OP_COLOR_BURN:
+      return SkXfermode::kColorBurn_Mode;
+    case OP_HARD_LIGHT:
+      return SkXfermode::kHardLight_Mode;
+    case OP_SOFT_LIGHT:
+      return SkXfermode::kSoftLight_Mode;
+    case OP_DIFFERENCE:
+      return SkXfermode::kDifference_Mode;
+    case OP_EXCLUSION:
+      return SkXfermode::kExclusion_Mode;
+    case OP_HUE:
+      return SkXfermode::kHue_Mode;
+    case OP_SATURATION:
+      return SkXfermode::kSaturation_Mode;
+    case OP_COLOR:
+      return SkXfermode::kColor_Mode;
+    case OP_LUMINOSITY:
+      return SkXfermode::kLuminosity_Mode;
+    default:
       return SkXfermode::kSrcOver_Mode;
   }
-  return SkXfermode::kSrcOver_Mode;
 }
 
 static inline SkColor ColorToSkColor(const Color &color, Float aAlpha)
 {
   //XXX: do a better job converting to int
   return SkColorSetARGB(U8CPU(color.a*aAlpha*255.0), U8CPU(color.r*255.0),
                         U8CPU(color.g*255.0), U8CPU(color.b*255.0));
 }
--- a/gfx/2d/PathSkia.cpp
+++ b/gfx/2d/PathSkia.cpp
@@ -131,18 +131,25 @@ PathSkia::ContainsPoint(const Point &aPo
 
   Rect bounds = GetBounds(aTransform);
 
   if (aPoint.x < bounds.x || aPoint.y < bounds.y ||
       aPoint.x > bounds.XMost() || aPoint.y > bounds.YMost()) {
     return false;
   }
 
-  return mPath.contains(SkFloatToScalar(transformed.x),
-                        SkFloatToScalar(transformed.y));
+  SkRegion pointRect;
+  pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1)),
+                    int32_t(SkFloatToScalar(transformed.y - 1)),
+                    int32_t(SkFloatToScalar(transformed.x + 1)),
+                    int32_t(SkFloatToScalar(transformed.y + 1)));
+
+  SkRegion pathRegion;
+  
+  return pathRegion.setPath(mPath, pointRect);
 }
 
 static Rect SkRectToRect(const SkRect& aBounds)
 {
   return Rect(SkScalarToFloat(aBounds.fLeft),
               SkScalarToFloat(aBounds.fTop),
               SkScalarToFloat(aBounds.fRight - aBounds.fLeft),
               SkScalarToFloat(aBounds.fBottom - aBounds.fTop));
@@ -165,18 +172,25 @@ PathSkia::StrokeContainsPoint(const Stro
 
   Rect bounds = aTransform.TransformBounds(SkRectToRect(strokePath.getBounds()));
 
   if (aPoint.x < bounds.x || aPoint.y < bounds.y ||
       aPoint.x > bounds.XMost() || aPoint.y > bounds.YMost()) {
     return false;
   }
 
-  return strokePath.contains(SkFloatToScalar(transformed.x),
-                             SkFloatToScalar(transformed.y));
+  SkRegion pointRect;
+  pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1)),
+                    int32_t(SkFloatToScalar(transformed.y - 1)),
+                    int32_t(SkFloatToScalar(transformed.x + 1)),
+                    int32_t(SkFloatToScalar(transformed.y + 1)));
+
+  SkRegion pathRegion;
+  
+  return pathRegion.setPath(strokePath, pointRect);
 }
 
 Rect
 PathSkia::GetBounds(const Matrix &aTransform) const
 {
   Rect bounds = SkRectToRect(mPath.getBounds());
   return aTransform.TransformBounds(bounds);
 }
--- a/gfx/layers/ipc/ImageContainerParent.cpp
+++ b/gfx/layers/ipc/ImageContainerParent.cpp
@@ -34,17 +34,16 @@ bool ImageContainerParent::RecvPublishIm
   }
   return true;
 }
 
 bool ImageContainerParent::RecvFlush()
 {
   SharedImage *img = RemoveSharedImage(mID);
   if (img) {
-    DeallocSharedImageData(this, *img);
     delete img;
   }
   return true;
 }
 
 void ImageContainerParent::DoStop()
 {
   mStop = true;
--- a/gfx/skia/include/core/SkXfermode.h
+++ b/gfx/skia/include/core/SkXfermode.h
@@ -96,33 +96,37 @@ public:
         kDstOut_Mode,   //!< [Da * (1 - Sa), Dc * (1 - Sa)]
         kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]
         kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
         kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
 
         // all remaining modes are defined in the SVG Compositing standard
         // http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/
         kPlus_Mode,
-        kMultiply_Mode,
 
         // all above modes can be expressed as pair of src/dst Coeffs
         kCoeffModesCnt,
 
-        kScreen_Mode = kCoeffModesCnt,
+        kMultiply_Mode = kCoeffModesCnt,
+        kScreen_Mode,
         kOverlay_Mode,
         kDarken_Mode,
         kLighten_Mode,
         kColorDodge_Mode,
         kColorBurn_Mode,
         kHardLight_Mode,
         kSoftLight_Mode,
         kDifference_Mode,
         kExclusion_Mode,
+        kHue_Mode,
+        kSaturation_Mode,
+        kColor_Mode,
+        kLuminosity_Mode,
 
-        kLastMode = kExclusion_Mode
+        kLastMode = kLuminosity_Mode
     };
 
     /**
      *  If the xfermode is one of the modes in the Mode enum, then asMode()
      *  returns true and sets (if not null) mode accordingly. Otherwise it
      *  returns false and ignores the mode parameter.
      */
     virtual bool asMode(Mode* mode);
new file mode 100644
--- /dev/null
+++ b/gfx/skia/patches/0010-Bug-836892-Add-new-blending-modes-to-SkXfermode.patch
@@ -0,0 +1,698 @@
+# HG changeset patch
+# User Rik Cabanier <cabanier@adobe.com>
+# Date 1360273929 -46800
+# Node ID 3ac8edca3a03b3d22240b5a5b95ae3b5ada9877d
+# Parent  cbb67fe70b864b36165061e1fd3b083cd09af087
+Bug 836892 - Add new blending modes to SkXfermode. r=gw280
+
+diff --git a/gfx/skia/include/core/SkXfermode.h b/gfx/skia/include/core/SkXfermode.h
+--- a/gfx/skia/include/core/SkXfermode.h
++++ b/gfx/skia/include/core/SkXfermode.h
+@@ -96,33 +96,37 @@ public:
+         kDstOut_Mode,   //!< [Da * (1 - Sa), Dc * (1 - Sa)]
+         kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]
+         kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
+         kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
+ 
+         // all remaining modes are defined in the SVG Compositing standard
+         // http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/
+         kPlus_Mode,
+-        kMultiply_Mode,
+ 
+         // all above modes can be expressed as pair of src/dst Coeffs
+         kCoeffModesCnt,
+ 
+-        kScreen_Mode = kCoeffModesCnt,
++        kMultiply_Mode = kCoeffModesCnt,
++        kScreen_Mode,
+         kOverlay_Mode,
+         kDarken_Mode,
+         kLighten_Mode,
+         kColorDodge_Mode,
+         kColorBurn_Mode,
+         kHardLight_Mode,
+         kSoftLight_Mode,
+         kDifference_Mode,
+         kExclusion_Mode,
++        kHue_Mode,
++        kSaturation_Mode,
++        kColor_Mode,
++        kLuminosity_Mode,
+ 
+-        kLastMode = kExclusion_Mode
++        kLastMode = kLuminosity_Mode
+     };
+ 
+     /**
+      *  If the xfermode is one of the modes in the Mode enum, then asMode()
+      *  returns true and sets (if not null) mode accordingly. Otherwise it
+      *  returns false and ignores the mode parameter.
+      */
+     virtual bool asMode(Mode* mode);
+diff --git a/gfx/skia/src/core/SkXfermode.cpp b/gfx/skia/src/core/SkXfermode.cpp
+--- a/gfx/skia/src/core/SkXfermode.cpp
++++ b/gfx/skia/src/core/SkXfermode.cpp
+@@ -7,16 +7,18 @@
+  */
+ 
+ 
+ #include "SkXfermode.h"
+ #include "SkColorPriv.h"
+ #include "SkFlattenableBuffers.h"
+ #include "SkMathPriv.h"
+ 
++#include <algorithm>
++
+ SK_DEFINE_INST_COUNT(SkXfermode)
+ 
+ #define SkAlphaMulAlpha(a, b)   SkMulDiv255Round(a, b)
+ 
+ #if 0
+ // idea for higher precision blends in xfer procs (and slightly faster)
+ // see DstATop as a probable caller
+ static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
+@@ -176,244 +178,439 @@ static SkPMColor xor_modeproc(SkPMColor 
+ static SkPMColor plus_modeproc(SkPMColor src, SkPMColor dst) {
+     unsigned b = saturated_add(SkGetPackedB32(src), SkGetPackedB32(dst));
+     unsigned g = saturated_add(SkGetPackedG32(src), SkGetPackedG32(dst));
+     unsigned r = saturated_add(SkGetPackedR32(src), SkGetPackedR32(dst));
+     unsigned a = saturated_add(SkGetPackedA32(src), SkGetPackedA32(dst));
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
++static inline int srcover_byte(int a, int b) {
++    return a + b - SkAlphaMulAlpha(a, b);
++}
++
++#define  blendfunc_byte(sc, dc, sa, da, blendfunc) \
++  clamp_div255round(sc * (255 - da)  + dc * (255 - sa)  + blendfunc(sc, dc, sa, da))
++
+ // kMultiply_Mode
++static inline int multiply_byte(int sc, int dc, int sa, int da) {
++    return sc * dc;
++}
+ static SkPMColor multiply_modeproc(SkPMColor src, SkPMColor dst) {
+-    int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
+-    int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
+-    int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
+-    int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
++    int sa = SkGetPackedA32(src);
++    int da = SkGetPackedA32(dst);
++    int a = srcover_byte(sa, da);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, multiply_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, multiply_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, multiply_byte);
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
+ // kScreen_Mode
+-static inline int srcover_byte(int a, int b) {
+-    return a + b - SkAlphaMulAlpha(a, b);
++static inline int screen_byte(int sc, int dc, int sa, int da) {
++    return sc * da + sa * dc - sc * dc;
+ }
+ static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
+-    int a = srcover_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
+-    int r = srcover_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
+-    int g = srcover_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
+-    int b = srcover_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
++    int sa = SkGetPackedA32(src);
++    int da = SkGetPackedA32(dst);
++    int a = srcover_byte(sa, da);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, screen_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, screen_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, screen_byte);
++    return SkPackARGB32(a, r, g, b);
++}
++
++// kHardLight_Mode
++static inline int hardlight_byte(int sc, int dc, int sa, int da) {
++    if(!sa || !da)
++        return sc * da;
++    float Sc = (float)sc/sa;
++    float Dc = (float)dc/da;
++    if(Sc <= 0.5)
++        Sc *= 2 * Dc;
++    else
++        Sc = -1 + 2 * Sc + 2 * Dc - 2 * Sc * Dc;
++
++    return Sc * sa * da;
++}
++static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
++    int sa = SkGetPackedA32(src);
++    int da = SkGetPackedA32(dst);
++    int a = srcover_byte(sa, da);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, hardlight_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, hardlight_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, hardlight_byte);
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
+ // kOverlay_Mode
+ static inline int overlay_byte(int sc, int dc, int sa, int da) {
+-    int tmp = sc * (255 - da) + dc * (255 - sa);
+-    int rc;
+-    if (2 * dc <= da) {
+-        rc = 2 * sc * dc;
+-    } else {
+-        rc = sa * da - 2 * (da - dc) * (sa - sc);
+-    }
+-    return clamp_div255round(rc + tmp);
++    return hardlight_byte(dc, sc, da, sa);
+ }
+ static SkPMColor overlay_modeproc(SkPMColor src, SkPMColor dst) {
+     int sa = SkGetPackedA32(src);
+     int da = SkGetPackedA32(dst);
+     int a = srcover_byte(sa, da);
+-    int r = overlay_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+-    int g = overlay_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+-    int b = overlay_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, overlay_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, overlay_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, overlay_byte);
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
+ // kDarken_Mode
+ static inline int darken_byte(int sc, int dc, int sa, int da) {
+-    int sd = sc * da;
+-    int ds = dc * sa;
+-    if (sd < ds) {
+-        // srcover
+-        return sc + dc - SkDiv255Round(ds);
+-    } else {
+-        // dstover
+-        return dc + sc - SkDiv255Round(sd);
+-    }
++    return SkMin32(sc * da, sa * dc);
+ }
+ static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) {
+     int sa = SkGetPackedA32(src);
+     int da = SkGetPackedA32(dst);
+     int a = srcover_byte(sa, da);
+-    int r = darken_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+-    int g = darken_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+-    int b = darken_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, darken_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, darken_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, darken_byte);
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
+ // kLighten_Mode
+ static inline int lighten_byte(int sc, int dc, int sa, int da) {
+-    int sd = sc * da;
+-    int ds = dc * sa;
+-    if (sd > ds) {
+-        // srcover
+-        return sc + dc - SkDiv255Round(ds);
+-    } else {
+-        // dstover
+-        return dc + sc - SkDiv255Round(sd);
+-    }
++    return SkMax32(sc * da, sa * dc);
+ }
+ static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) {
+     int sa = SkGetPackedA32(src);
+     int da = SkGetPackedA32(dst);
+     int a = srcover_byte(sa, da);
+-    int r = lighten_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+-    int g = lighten_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+-    int b = lighten_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, lighten_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, lighten_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, lighten_byte);
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
+ // kColorDodge_Mode
+ static inline int colordodge_byte(int sc, int dc, int sa, int da) {
+-    int diff = sa - sc;
+-    int rc;
+-    if (0 == diff) {
+-        rc = sa * da + sc * (255 - da) + dc * (255 - sa);
+-        rc = SkDiv255Round(rc);
+-    } else {
+-        int tmp = (dc * sa << 15) / (da * diff);
+-        rc = SkDiv255Round(sa * da) * tmp >> 15;
+-        // don't clamp here, since we'll do it in our modeproc
+-    }
+-    return rc;
++    if (dc == 0)
++        return 0;
++    // Avoid division by 0
++    if (sc == sa)
++        return da * sa;
++
++    return SkMin32(sa * da, sa * sa * dc / (sa - sc));
+ }
+ static SkPMColor colordodge_modeproc(SkPMColor src, SkPMColor dst) {
+-    // added to avoid div-by-zero in colordodge_byte
+-    if (0 == dst) {
+-        return src;
+-    }
+-
+     int sa = SkGetPackedA32(src);
+     int da = SkGetPackedA32(dst);
+     int a = srcover_byte(sa, da);
+-    int r = colordodge_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+-    int g = colordodge_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+-    int b = colordodge_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+-    r = clamp_max(r, a);
+-    g = clamp_max(g, a);
+-    b = clamp_max(b, a);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, colordodge_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, colordodge_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, colordodge_byte);
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
+ // kColorBurn_Mode
+ static inline int colorburn_byte(int sc, int dc, int sa, int da) {
+-    int rc;
+-    if (dc == da && 0 == sc) {
+-        rc = sa * da + dc * (255 - sa);
+-    } else if (0 == sc) {
+-        return SkAlphaMulAlpha(dc, 255 - sa);
+-    } else {
+-        int tmp = (sa * (da - dc) * 256) / (sc * da);
+-        if (tmp > 256) {
+-            tmp = 256;
+-        }
+-        int tmp2 = sa * da;
+-        rc = tmp2 - (tmp2 * tmp >> 8) + sc * (255 - da) + dc * (255 - sa);
+-    }
+-    return SkDiv255Round(rc);
++    // Avoid division by 0
++    if(dc == da)
++        return sa * da;
++    if(sc == 0)
++        return 0;
++
++    return sa * da - SkMin32(sa * da, sa * sa * (da - dc) / sc);
+ }
+ static SkPMColor colorburn_modeproc(SkPMColor src, SkPMColor dst) {
+-    // added to avoid div-by-zero in colorburn_byte
+-    if (0 == dst) {
+-        return src;
+-    }
+-
+     int sa = SkGetPackedA32(src);
+     int da = SkGetPackedA32(dst);
+     int a = srcover_byte(sa, da);
+-    int r = colorburn_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+-    int g = colorburn_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+-    int b = colorburn_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+-    return SkPackARGB32(a, r, g, b);
+-}
+-
+-// kHardLight_Mode
+-static inline int hardlight_byte(int sc, int dc, int sa, int da) {
+-    int rc;
+-    if (2 * sc <= sa) {
+-        rc = 2 * sc * dc;
+-    } else {
+-        rc = sa * da - 2 * (da - dc) * (sa - sc);
+-    }
+-    return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
+-}
+-static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
+-    int sa = SkGetPackedA32(src);
+-    int da = SkGetPackedA32(dst);
+-    int a = srcover_byte(sa, da);
+-    int r = hardlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+-    int g = hardlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+-    int b = hardlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, colorburn_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, colorburn_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, colorburn_byte);
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
+ // returns 255 * sqrt(n/255)
+ static U8CPU sqrt_unit_byte(U8CPU n) {
+     return SkSqrtBits(n, 15+4);
+ }
+ 
+ // kSoftLight_Mode
+ static inline int softlight_byte(int sc, int dc, int sa, int da) {
+     int m = da ? dc * 256 / da : 0;
+     int rc;
+-    if (2 * sc <= sa) {
+-        rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
+-    } else if (4 * dc <= da) {
++    if (2 * sc <= sa)
++       return dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
++
++    if (4 * dc <= da) {
+         int tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m;
+-        rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
+-    } else {
+-        int tmp = sqrt_unit_byte(m) - m;
+-        rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
++        return dc * sa + (da * (2 * sc - sa) * tmp >> 8);
+     }
+-    return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
++    int tmp = sqrt_unit_byte(m) - m;
++    return rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
+ }
+ static SkPMColor softlight_modeproc(SkPMColor src, SkPMColor dst) {
+     int sa = SkGetPackedA32(src);
+     int da = SkGetPackedA32(dst);
+     int a = srcover_byte(sa, da);
+-    int r = softlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+-    int g = softlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+-    int b = softlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, softlight_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, softlight_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, softlight_byte);
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
+ // kDifference_Mode
+ static inline int difference_byte(int sc, int dc, int sa, int da) {
+-    int tmp = SkMin32(sc * da, dc * sa);
+-    return clamp_signed_byte(sc + dc - 2 * SkDiv255Round(tmp));
++    int tmp = dc * sa - sc * da;
++    if(tmp<0)
++        return - tmp;
++
++    return tmp;
+ }
+ static SkPMColor difference_modeproc(SkPMColor src, SkPMColor dst) {
+     int sa = SkGetPackedA32(src);
+     int da = SkGetPackedA32(dst);
+     int a = srcover_byte(sa, da);
+-    int r = difference_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+-    int g = difference_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+-    int b = difference_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, difference_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, difference_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, difference_byte);
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
+ // kExclusion_Mode
+ static inline int exclusion_byte(int sc, int dc, int sa, int da) {
+-    // this equations is wacky, wait for SVG to confirm it
+-    int r = sc * da + dc * sa - 2 * sc * dc + sc * (255 - da) + dc * (255 - sa);
+-    return clamp_div255round(r);
++    return sc * da + dc * sa - 2 * dc * sc;
+ }
+ static SkPMColor exclusion_modeproc(SkPMColor src, SkPMColor dst) {
+     int sa = SkGetPackedA32(src);
+     int da = SkGetPackedA32(dst);
+     int a = srcover_byte(sa, da);
+-    int r = exclusion_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+-    int g = exclusion_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+-    int b = exclusion_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
++    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, exclusion_byte);
++    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, exclusion_byte);
++    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, exclusion_byte);
++    return SkPackARGB32(a, r, g, b);
++}
++
++///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
++struct BlendColor {
++    float r;
++    float g;
++    float b;
++
++    BlendColor(): r(0), g(0), b(0)
++      {}
++};
++
++static inline float Lum(BlendColor C)
++{
++    return C.r * 0.3 + C.g * 0.59 + C.b* 0.11;
++}
++
++static inline float SkMinFloat(float a, float b)
++{
++  if (a > b)
++    a = b;
++  return a;
++}
++
++static inline float SkMaxFloat(float a, float b)
++{
++  if (a < b)
++    a = b;
++  return a;
++}
++
++#define minimum(C) SkMinFloat(SkMinFloat(C.r, C.g), C.b)
++#define maximum(C) SkMaxFloat(SkMaxFloat(C.r, C.g), C.b)
++
++static inline float Sat(BlendColor c) {
++    return maximum(c) - minimum(c);
++}
++
++static inline void setSaturationComponents(float& Cmin, float& Cmid, float& Cmax, float s) {
++    if(Cmax > Cmin) {
++        Cmid =  (((Cmid - Cmin) * s ) / (Cmax - Cmin));
++        Cmax = s;
++    } else {
++        Cmax = 0;
++        Cmid = 0;
++    }
++    Cmin = 0;
++}
++
++static inline BlendColor SetSat(BlendColor C, float s) {
++    if(C.r <= C.g) {
++        if(C.g <= C.b)
++            setSaturationComponents(C.r, C.g, C.b, s);
++        else
++        if(C.r <= C.b)
++            setSaturationComponents(C.r, C.b, C.g, s);
++        else
++            setSaturationComponents(C.b, C.r, C.g, s);
++        } else if(C.r <= C.b)
++            setSaturationComponents(C.g, C.r, C.b, s);
++        else
++        if(C.g <= C.b)
++            setSaturationComponents(C.g, C.b, C.r, s);
++        else
++            setSaturationComponents(C.b, C.g, C.r, s);
++
++        return C;
++}
++
++static inline BlendColor clipColor(BlendColor C) {
++    float L = Lum(C);
++    float n = minimum(C);
++    float x = maximum(C);
++    if(n < 0) {
++       C.r = L + (((C.r - L) * L) / (L - n));
++       C.g = L + (((C.g - L) * L) / (L - n));
++       C.b = L + (((C.b - L) * L) / (L - n));
++    }
++
++    if(x > 1) {
++       C.r = L + (((C.r - L) * (1 - L)) / (x - L));
++       C.g = L + (((C.g - L) * (1 - L)) / (x - L));
++       C.b = L + (((C.b - L) * (1 - L)) / (x - L));
++    }
++    return C;
++}
++
++static inline BlendColor SetLum(BlendColor C, float l) {
++  float d = l - Lum(C);
++  C.r +=  d;
++  C.g +=  d;
++  C.b +=  d;
++
++  return clipColor(C);
++}
++
++#define  blendfunc_nonsep_byte(sc, dc, sa, da, blendval) \
++  clamp_div255round(sc * (255 - da)  + dc * (255 - sa)  +  (int)(sa * da * blendval))
++
++static SkPMColor hue_modeproc(SkPMColor src, SkPMColor dst) {
++    int sr = SkGetPackedR32(src);
++    int sg = SkGetPackedG32(src);
++    int sb = SkGetPackedB32(src);
++    int sa = SkGetPackedA32(src);
++
++    int dr = SkGetPackedR32(dst);
++    int dg = SkGetPackedG32(dst);
++    int db = SkGetPackedB32(dst);
++    int da = SkGetPackedA32(dst);
++
++    BlendColor Cs;
++    if(sa) {
++        Cs.r  = (float)sr / sa;
++        Cs.g = (float)sg / sa;
++        Cs.b = (float)sb / sa;
++        BlendColor Cd;
++        if(da) {
++            Cd.r =  (float)dr / da;
++            Cd.g = (float)dg / da;
++            Cd.b = (float)db / da;
++            Cs = SetLum(SetSat(Cs, Sat(Cd)), Lum(Cd));
++        }
++    }
++
++    int a = srcover_byte(sa, da);
++    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
++    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
++    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
++    return SkPackARGB32(a, r, g, b);
++}
++
++static SkPMColor saturation_modeproc(SkPMColor src, SkPMColor dst) {
++    int sr = SkGetPackedR32(src);
++    int sg = SkGetPackedG32(src);
++    int sb = SkGetPackedB32(src);
++    int sa = SkGetPackedA32(src);
++
++    int dr = SkGetPackedR32(dst);
++    int dg = SkGetPackedG32(dst);
++    int db = SkGetPackedB32(dst);
++    int da = SkGetPackedA32(dst);
++
++    BlendColor Cs;
++    if(sa) {
++        Cs.r  = (float)sr / sa;
++        Cs.g = (float)sg / sa;
++        Cs.b = (float)sb / sa;
++        BlendColor Cd;
++        if(da) {
++            Cd.r =  (float)dr / da;
++            Cd.g = (float)dg / da;
++            Cd.b = (float)db / da;
++            Cs = SetLum(SetSat(Cd, Sat(Cs)), Lum(Cd));
++        }
++    }
++
++    int a = srcover_byte(sa, da);
++    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
++    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
++    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
++    return SkPackARGB32(a, r, g, b);
++}
++
++static SkPMColor color_modeproc(SkPMColor src, SkPMColor dst) {
++    int sr = SkGetPackedR32(src);
++    int sg = SkGetPackedG32(src);
++    int sb = SkGetPackedB32(src);
++    int sa = SkGetPackedA32(src);
++
++    int dr = SkGetPackedR32(dst);
++    int dg = SkGetPackedG32(dst);
++    int db = SkGetPackedB32(dst);
++    int da = SkGetPackedA32(dst);
++
++    BlendColor Cs;
++    if(sa) {
++        Cs.r  = (float)sr / sa;
++        Cs.g = (float)sg / sa;
++        Cs.b = (float)sb / sa;
++        BlendColor Cd;
++        if(da) {
++            Cd.r =  (float)dr / da;
++            Cd.g = (float)dg / da;
++            Cd.b = (float)db / da;
++            Cs = SetLum(Cs, Lum(Cd));
++            }
++    }
++
++    int a = srcover_byte(sa, da);
++    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
++    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
++    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
++    return SkPackARGB32(a, r, g, b);
++}
++
++static SkPMColor luminosity_modeproc(SkPMColor src, SkPMColor dst) {
++    int sr = SkGetPackedR32(src);
++    int sg = SkGetPackedG32(src);
++    int sb = SkGetPackedB32(src);
++    int sa = SkGetPackedA32(src);
++
++    int dr = SkGetPackedR32(dst);
++    int dg = SkGetPackedG32(dst);
++    int db = SkGetPackedB32(dst);
++    int da = SkGetPackedA32(dst);
++
++    BlendColor Cs;
++    if(sa) {
++        Cs.r  = (float)sr / sa;
++        Cs.g = (float)sg / sa;
++        Cs.b = (float)sb / sa;
++        BlendColor Cd;
++        if(da) {
++            Cd.r =  (float)dr / da;
++            Cd.g = (float)dg / da;
++            Cd.b = (float)db / da;
++            Cs = SetLum(Cd, Lum(Cs));
++            }
++    }
++
++    int a = srcover_byte(sa, da);
++    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
++    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
++    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
+     return SkPackARGB32(a, r, g, b);
+ }
+ 
+ struct ProcCoeff {
+     SkXfermodeProc      fProc;
+     SkXfermode::Coeff   fSC;
+     SkXfermode::Coeff   fDC;
+ };
+@@ -430,27 +627,31 @@ static const ProcCoeff gProcCoeffs[] = {
+     { dstin_modeproc,   SkXfermode::kZero_Coeff,    SkXfermode::kSA_Coeff },
+     { srcout_modeproc,  SkXfermode::kIDA_Coeff,     SkXfermode::kZero_Coeff },
+     { dstout_modeproc,  SkXfermode::kZero_Coeff,    SkXfermode::kISA_Coeff },
+     { srcatop_modeproc, SkXfermode::kDA_Coeff,      SkXfermode::kISA_Coeff },
+     { dstatop_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kSA_Coeff },
+     { xor_modeproc,     SkXfermode::kIDA_Coeff,     SkXfermode::kISA_Coeff },
+ 
+     { plus_modeproc,    SkXfermode::kOne_Coeff,     SkXfermode::kOne_Coeff },
+-    { multiply_modeproc,SkXfermode::kZero_Coeff,    SkXfermode::kSC_Coeff },
++    { multiply_modeproc,    CANNOT_USE_COEFF,       CANNOT_USE_COEFF},
+     { screen_modeproc,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+     { overlay_modeproc,     CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+     { darken_modeproc,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+     { lighten_modeproc,     CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+     { colordodge_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+     { colorburn_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+     { hardlight_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+     { softlight_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+     { difference_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+     { exclusion_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
++    { hue_modeproc,         CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
++    { saturation_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
++    { color_modeproc,       CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
++    { luminosity_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+ };
+ 
+ ///////////////////////////////////////////////////////////////////////////////
+ 
+ bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) {
+     return false;
+ }
+ 
+@@ -1172,16 +1373,20 @@ static const Proc16Rec gModeProcs16[] = 
+     { darken_modeproc16_0,  darken_modeproc16_255,  NULL            }, // darken
+     { lighten_modeproc16_0, lighten_modeproc16_255, NULL            }, // lighten
+     { NULL,                 NULL,                   NULL            }, // colordodge
+     { NULL,                 NULL,                   NULL            }, // colorburn
+     { NULL,                 NULL,                   NULL            }, // hardlight
+     { NULL,                 NULL,                   NULL            }, // softlight
+     { NULL,                 NULL,                   NULL            }, // difference
+     { NULL,                 NULL,                   NULL            }, // exclusion
++    { NULL,                 NULL,                   NULL            }, // hue
++    { NULL,                 NULL,                   NULL            }, // saturation
++    { NULL,                 NULL,                   NULL            }, // color
++    { NULL,                 NULL,                   NULL            }, // luminosity
+ };
+ 
+ SkXfermodeProc16 SkXfermode::GetProc16(Mode mode, SkColor srcColor) {
+     SkXfermodeProc16  proc16 = NULL;
+     if ((unsigned)mode < kModeCount) {
+         const Proc16Rec& rec = gModeProcs16[mode];
+         unsigned a = SkColorGetA(srcColor);
+ 
--- a/gfx/skia/patches/README
+++ b/gfx/skia/patches/README
@@ -2,8 +2,9 @@ This directory contains the patches curr
 The original patches are archived in archive/
 
 See the relevant bugs in bugzilla for information on these patches:
 
 0001-Bug-777614-Re-add-our-SkUserConfig.h-r-nrc.patch
 0004-Bug-777614-Re-apply-bug-719872-Fix-crash-on-Android-.patch
 0005-Bug-777614-Re-apply-bug-687188-Expand-the-gradient-c.patch
 0009-Bug-777614-Re-apply-759683-Handle-compilers-that-don.patch
+0010-Bug-836892-Add-new-blending-modes-to-SkXfermode.patch
--- a/gfx/skia/src/core/SkXfermode.cpp
+++ b/gfx/skia/src/core/SkXfermode.cpp
@@ -7,16 +7,18 @@
  */
 
 
 #include "SkXfermode.h"
 #include "SkColorPriv.h"
 #include "SkFlattenableBuffers.h"
 #include "SkMathPriv.h"
 
+#include <algorithm>
+
 SK_DEFINE_INST_COUNT(SkXfermode)
 
 #define SkAlphaMulAlpha(a, b)   SkMulDiv255Round(a, b)
 
 #if 0
 // idea for higher precision blends in xfer procs (and slightly faster)
 // see DstATop as a probable caller
 static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
@@ -176,244 +178,439 @@ static SkPMColor xor_modeproc(SkPMColor 
 static SkPMColor plus_modeproc(SkPMColor src, SkPMColor dst) {
     unsigned b = saturated_add(SkGetPackedB32(src), SkGetPackedB32(dst));
     unsigned g = saturated_add(SkGetPackedG32(src), SkGetPackedG32(dst));
     unsigned r = saturated_add(SkGetPackedR32(src), SkGetPackedR32(dst));
     unsigned a = saturated_add(SkGetPackedA32(src), SkGetPackedA32(dst));
     return SkPackARGB32(a, r, g, b);
 }
 
+static inline int srcover_byte(int a, int b) {
+    return a + b - SkAlphaMulAlpha(a, b);
+}
+
+#define  blendfunc_byte(sc, dc, sa, da, blendfunc) \
+  clamp_div255round(sc * (255 - da)  + dc * (255 - sa)  + blendfunc(sc, dc, sa, da))
+
 // kMultiply_Mode
+static inline int multiply_byte(int sc, int dc, int sa, int da) {
+    return sc * dc;
+}
 static SkPMColor multiply_modeproc(SkPMColor src, SkPMColor dst) {
-    int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
-    int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
-    int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
-    int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, multiply_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, multiply_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, multiply_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kScreen_Mode
-static inline int srcover_byte(int a, int b) {
-    return a + b - SkAlphaMulAlpha(a, b);
+static inline int screen_byte(int sc, int dc, int sa, int da) {
+    return sc * da + sa * dc - sc * dc;
 }
 static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
-    int a = srcover_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
-    int r = srcover_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
-    int g = srcover_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
-    int b = srcover_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, screen_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, screen_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, screen_byte);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kHardLight_Mode
+static inline int hardlight_byte(int sc, int dc, int sa, int da) {
+    if(!sa || !da)
+        return sc * da;
+    float Sc = (float)sc/sa;
+    float Dc = (float)dc/da;
+    if(Sc <= 0.5)
+        Sc *= 2 * Dc;
+    else
+        Sc = -1 + 2 * Sc + 2 * Dc - 2 * Sc * Dc;
+
+    return Sc * sa * da;
+}
+static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, hardlight_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, hardlight_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, hardlight_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kOverlay_Mode
 static inline int overlay_byte(int sc, int dc, int sa, int da) {
-    int tmp = sc * (255 - da) + dc * (255 - sa);
-    int rc;
-    if (2 * dc <= da) {
-        rc = 2 * sc * dc;
-    } else {
-        rc = sa * da - 2 * (da - dc) * (sa - sc);
-    }
-    return clamp_div255round(rc + tmp);
+    return hardlight_byte(dc, sc, da, sa);
 }
 static SkPMColor overlay_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = overlay_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = overlay_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = overlay_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, overlay_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, overlay_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, overlay_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kDarken_Mode
 static inline int darken_byte(int sc, int dc, int sa, int da) {
-    int sd = sc * da;
-    int ds = dc * sa;
-    if (sd < ds) {
-        // srcover
-        return sc + dc - SkDiv255Round(ds);
-    } else {
-        // dstover
-        return dc + sc - SkDiv255Round(sd);
-    }
+    return SkMin32(sc * da, sa * dc);
 }
 static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = darken_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = darken_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = darken_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, darken_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, darken_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, darken_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kLighten_Mode
 static inline int lighten_byte(int sc, int dc, int sa, int da) {
-    int sd = sc * da;
-    int ds = dc * sa;
-    if (sd > ds) {
-        // srcover
-        return sc + dc - SkDiv255Round(ds);
-    } else {
-        // dstover
-        return dc + sc - SkDiv255Round(sd);
-    }
+    return SkMax32(sc * da, sa * dc);
 }
 static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = lighten_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = lighten_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = lighten_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, lighten_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, lighten_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, lighten_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kColorDodge_Mode
 static inline int colordodge_byte(int sc, int dc, int sa, int da) {
-    int diff = sa - sc;
-    int rc;
-    if (0 == diff) {
-        rc = sa * da + sc * (255 - da) + dc * (255 - sa);
-        rc = SkDiv255Round(rc);
-    } else {
-        int tmp = (dc * sa << 15) / (da * diff);
-        rc = SkDiv255Round(sa * da) * tmp >> 15;
-        // don't clamp here, since we'll do it in our modeproc
-    }
-    return rc;
+    if (dc == 0)
+        return 0;
+    // Avoid division by 0
+    if (sc == sa)
+        return da * sa;
+
+    return SkMin32(sa * da, sa * sa * dc / (sa - sc));
 }
 static SkPMColor colordodge_modeproc(SkPMColor src, SkPMColor dst) {
-    // added to avoid div-by-zero in colordodge_byte
-    if (0 == dst) {
-        return src;
-    }
-
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = colordodge_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = colordodge_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = colordodge_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
-    r = clamp_max(r, a);
-    g = clamp_max(g, a);
-    b = clamp_max(b, a);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, colordodge_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, colordodge_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, colordodge_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kColorBurn_Mode
 static inline int colorburn_byte(int sc, int dc, int sa, int da) {
-    int rc;
-    if (dc == da && 0 == sc) {
-        rc = sa * da + dc * (255 - sa);
-    } else if (0 == sc) {
-        return SkAlphaMulAlpha(dc, 255 - sa);
-    } else {
-        int tmp = (sa * (da - dc) * 256) / (sc * da);
-        if (tmp > 256) {
-            tmp = 256;
-        }
-        int tmp2 = sa * da;
-        rc = tmp2 - (tmp2 * tmp >> 8) + sc * (255 - da) + dc * (255 - sa);
-    }
-    return SkDiv255Round(rc);
+    // Avoid division by 0
+    if(dc == da)
+        return sa * da;
+    if(sc == 0)
+        return 0;
+
+    return sa * da - SkMin32(sa * da, sa * sa * (da - dc) / sc);
 }
 static SkPMColor colorburn_modeproc(SkPMColor src, SkPMColor dst) {
-    // added to avoid div-by-zero in colorburn_byte
-    if (0 == dst) {
-        return src;
-    }
-
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = colorburn_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = colorburn_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = colorburn_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
-    return SkPackARGB32(a, r, g, b);
-}
-
-// kHardLight_Mode
-static inline int hardlight_byte(int sc, int dc, int sa, int da) {
-    int rc;
-    if (2 * sc <= sa) {
-        rc = 2 * sc * dc;
-    } else {
-        rc = sa * da - 2 * (da - dc) * (sa - sc);
-    }
-    return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
-}
-static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
-    int sa = SkGetPackedA32(src);
-    int da = SkGetPackedA32(dst);
-    int a = srcover_byte(sa, da);
-    int r = hardlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = hardlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = hardlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, colorburn_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, colorburn_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, colorburn_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // returns 255 * sqrt(n/255)
 static U8CPU sqrt_unit_byte(U8CPU n) {
     return SkSqrtBits(n, 15+4);
 }
 
 // kSoftLight_Mode
 static inline int softlight_byte(int sc, int dc, int sa, int da) {
     int m = da ? dc * 256 / da : 0;
     int rc;
-    if (2 * sc <= sa) {
-        rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
-    } else if (4 * dc <= da) {
+    if (2 * sc <= sa)
+       return dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
+
+    if (4 * dc <= da) {
         int tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m;
-        rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
-    } else {
-        int tmp = sqrt_unit_byte(m) - m;
-        rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
+        return dc * sa + (da * (2 * sc - sa) * tmp >> 8);
     }
-    return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
+    int tmp = sqrt_unit_byte(m) - m;
+    return rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
 }
 static SkPMColor softlight_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = softlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = softlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = softlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, softlight_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, softlight_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, softlight_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kDifference_Mode
 static inline int difference_byte(int sc, int dc, int sa, int da) {
-    int tmp = SkMin32(sc * da, dc * sa);
-    return clamp_signed_byte(sc + dc - 2 * SkDiv255Round(tmp));
+    int tmp = dc * sa - sc * da;
+    if(tmp<0)
+        return - tmp;
+
+    return tmp;
 }
 static SkPMColor difference_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = difference_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = difference_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = difference_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, difference_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, difference_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, difference_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kExclusion_Mode
 static inline int exclusion_byte(int sc, int dc, int sa, int da) {
-    // this equations is wacky, wait for SVG to confirm it
-    int r = sc * da + dc * sa - 2 * sc * dc + sc * (255 - da) + dc * (255 - sa);
-    return clamp_div255round(r);
+    return sc * da + dc * sa - 2 * dc * sc;
 }
 static SkPMColor exclusion_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = exclusion_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = exclusion_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = exclusion_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, exclusion_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, exclusion_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, exclusion_byte);
+    return SkPackARGB32(a, r, g, b);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+struct BlendColor {
+    float r;
+    float g;
+    float b;
+
+    BlendColor(): r(0), g(0), b(0)
+      {}
+};
+
+static inline float Lum(BlendColor C)
+{
+    return C.r * 0.3 + C.g * 0.59 + C.b* 0.11;
+}
+
+static inline float SkMinFloat(float a, float b)
+{
+  if (a > b)
+    a = b;
+  return a;
+}
+
+static inline float SkMaxFloat(float a, float b)
+{
+  if (a < b)
+    a = b;
+  return a;
+}
+
+#define minimum(C) SkMinFloat(SkMinFloat(C.r, C.g), C.b)
+#define maximum(C) SkMaxFloat(SkMaxFloat(C.r, C.g), C.b)
+
+static inline float Sat(BlendColor c) {
+    return maximum(c) - minimum(c);
+}
+
+static inline void setSaturationComponents(float& Cmin, float& Cmid, float& Cmax, float s) {
+    if(Cmax > Cmin) {
+        Cmid =  (((Cmid - Cmin) * s ) / (Cmax - Cmin));
+        Cmax = s;
+    } else {
+        Cmax = 0;
+        Cmid = 0;
+    }
+    Cmin = 0;
+}
+
+static inline BlendColor SetSat(BlendColor C, float s) {
+    if(C.r <= C.g) {
+        if(C.g <= C.b)
+            setSaturationComponents(C.r, C.g, C.b, s);
+        else
+        if(C.r <= C.b)
+            setSaturationComponents(C.r, C.b, C.g, s);
+        else
+            setSaturationComponents(C.b, C.r, C.g, s);
+        } else if(C.r <= C.b)
+            setSaturationComponents(C.g, C.r, C.b, s);
+        else
+        if(C.g <= C.b)
+            setSaturationComponents(C.g, C.b, C.r, s);
+        else
+            setSaturationComponents(C.b, C.g, C.r, s);
+
+        return C;
+}
+
+static inline BlendColor clipColor(BlendColor C) {
+    float L = Lum(C);
+    float n = minimum(C);
+    float x = maximum(C);
+    if(n < 0) {
+       C.r = L + (((C.r - L) * L) / (L - n));
+       C.g = L + (((C.g - L) * L) / (L - n));
+       C.b = L + (((C.b - L) * L) / (L - n));
+    }
+
+    if(x > 1) {
+       C.r = L + (((C.r - L) * (1 - L)) / (x - L));
+       C.g = L + (((C.g - L) * (1 - L)) / (x - L));
+       C.b = L + (((C.b - L) * (1 - L)) / (x - L));
+    }
+    return C;
+}
+
+static inline BlendColor SetLum(BlendColor C, float l) {
+  float d = l - Lum(C);
+  C.r +=  d;
+  C.g +=  d;
+  C.b +=  d;
+
+  return clipColor(C);
+}
+
+#define  blendfunc_nonsep_byte(sc, dc, sa, da, blendval) \
+  clamp_div255round(sc * (255 - da)  + dc * (255 - sa)  +  (int)(sa * da * blendval))
+
+static SkPMColor hue_modeproc(SkPMColor src, SkPMColor dst) {
+    int sr = SkGetPackedR32(src);
+    int sg = SkGetPackedG32(src);
+    int sb = SkGetPackedB32(src);
+    int sa = SkGetPackedA32(src);
+
+    int dr = SkGetPackedR32(dst);
+    int dg = SkGetPackedG32(dst);
+    int db = SkGetPackedB32(dst);
+    int da = SkGetPackedA32(dst);
+
+    BlendColor Cs;
+    if(sa) {
+        Cs.r  = (float)sr / sa;
+        Cs.g = (float)sg / sa;
+        Cs.b = (float)sb / sa;
+        BlendColor Cd;
+        if(da) {
+            Cd.r =  (float)dr / da;
+            Cd.g = (float)dg / da;
+            Cd.b = (float)db / da;
+            Cs = SetLum(SetSat(Cs, Sat(Cd)), Lum(Cd));
+        }
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
+    return SkPackARGB32(a, r, g, b);
+}
+
+static SkPMColor saturation_modeproc(SkPMColor src, SkPMColor dst) {
+    int sr = SkGetPackedR32(src);
+    int sg = SkGetPackedG32(src);
+    int sb = SkGetPackedB32(src);
+    int sa = SkGetPackedA32(src);
+
+    int dr = SkGetPackedR32(dst);
+    int dg = SkGetPackedG32(dst);
+    int db = SkGetPackedB32(dst);
+    int da = SkGetPackedA32(dst);
+
+    BlendColor Cs;
+    if(sa) {
+        Cs.r  = (float)sr / sa;
+        Cs.g = (float)sg / sa;
+        Cs.b = (float)sb / sa;
+        BlendColor Cd;
+        if(da) {
+            Cd.r =  (float)dr / da;
+            Cd.g = (float)dg / da;
+            Cd.b = (float)db / da;
+            Cs = SetLum(SetSat(Cd, Sat(Cs)), Lum(Cd));
+        }
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
+    return SkPackARGB32(a, r, g, b);
+}
+
+static SkPMColor color_modeproc(SkPMColor src, SkPMColor dst) {
+    int sr = SkGetPackedR32(src);
+    int sg = SkGetPackedG32(src);
+    int sb = SkGetPackedB32(src);
+    int sa = SkGetPackedA32(src);
+
+    int dr = SkGetPackedR32(dst);
+    int dg = SkGetPackedG32(dst);
+    int db = SkGetPackedB32(dst);
+    int da = SkGetPackedA32(dst);
+
+    BlendColor Cs;
+    if(sa) {
+        Cs.r  = (float)sr / sa;
+        Cs.g = (float)sg / sa;
+        Cs.b = (float)sb / sa;
+        BlendColor Cd;
+        if(da) {
+            Cd.r =  (float)dr / da;
+            Cd.g = (float)dg / da;
+            Cd.b = (float)db / da;
+            Cs = SetLum(Cs, Lum(Cd));
+            }
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
+    return SkPackARGB32(a, r, g, b);
+}
+
+static SkPMColor luminosity_modeproc(SkPMColor src, SkPMColor dst) {
+    int sr = SkGetPackedR32(src);
+    int sg = SkGetPackedG32(src);
+    int sb = SkGetPackedB32(src);
+    int sa = SkGetPackedA32(src);
+
+    int dr = SkGetPackedR32(dst);
+    int dg = SkGetPackedG32(dst);
+    int db = SkGetPackedB32(dst);
+    int da = SkGetPackedA32(dst);
+
+    BlendColor Cs;
+    if(sa) {
+        Cs.r  = (float)sr / sa;
+        Cs.g = (float)sg / sa;
+        Cs.b = (float)sb / sa;
+        BlendColor Cd;
+        if(da) {
+            Cd.r =  (float)dr / da;
+            Cd.g = (float)dg / da;
+            Cd.b = (float)db / da;
+            Cs = SetLum(Cd, Lum(Cs));
+            }
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
     return SkPackARGB32(a, r, g, b);
 }
 
 struct ProcCoeff {
     SkXfermodeProc      fProc;
     SkXfermode::Coeff   fSC;
     SkXfermode::Coeff   fDC;
 };
@@ -430,27 +627,31 @@ static const ProcCoeff gProcCoeffs[] = {
     { dstin_modeproc,   SkXfermode::kZero_Coeff,    SkXfermode::kSA_Coeff },
     { srcout_modeproc,  SkXfermode::kIDA_Coeff,     SkXfermode::kZero_Coeff },
     { dstout_modeproc,  SkXfermode::kZero_Coeff,    SkXfermode::kISA_Coeff },
     { srcatop_modeproc, SkXfermode::kDA_Coeff,      SkXfermode::kISA_Coeff },
     { dstatop_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kSA_Coeff },
     { xor_modeproc,     SkXfermode::kIDA_Coeff,     SkXfermode::kISA_Coeff },
 
     { plus_modeproc,    SkXfermode::kOne_Coeff,     SkXfermode::kOne_Coeff },
-    { multiply_modeproc,SkXfermode::kZero_Coeff,    SkXfermode::kSC_Coeff },
+    { multiply_modeproc,    CANNOT_USE_COEFF,       CANNOT_USE_COEFF},
     { screen_modeproc,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { overlay_modeproc,     CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { darken_modeproc,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { lighten_modeproc,     CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { colordodge_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { colorburn_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { hardlight_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { softlight_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { difference_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { exclusion_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { hue_modeproc,         CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { saturation_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { color_modeproc,       CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { luminosity_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) {
     return false;
 }
 
@@ -1172,16 +1373,20 @@ static const Proc16Rec gModeProcs16[] = 
     { darken_modeproc16_0,  darken_modeproc16_255,  NULL            }, // darken
     { lighten_modeproc16_0, lighten_modeproc16_255, NULL            }, // lighten
     { NULL,                 NULL,                   NULL            }, // colordodge
     { NULL,                 NULL,                   NULL            }, // colorburn
     { NULL,                 NULL,                   NULL            }, // hardlight
     { NULL,                 NULL,                   NULL            }, // softlight
     { NULL,                 NULL,                   NULL            }, // difference
     { NULL,                 NULL,                   NULL            }, // exclusion
+    { NULL,                 NULL,                   NULL            }, // hue
+    { NULL,                 NULL,                   NULL            }, // saturation
+    { NULL,                 NULL,                   NULL            }, // color
+    { NULL,                 NULL,                   NULL            }, // luminosity
 };
 
 SkXfermodeProc16 SkXfermode::GetProc16(Mode mode, SkColor srcColor) {
     SkXfermodeProc16  proc16 = NULL;
     if ((unsigned)mode < kModeCount) {
         const Proc16Rec& rec = gModeProcs16[mode];
         unsigned a = SkColorGetA(srcColor);
 
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -133,16 +133,17 @@ CPPSRCS		= \
 		Stack.cpp \
 		String.cpp \
 		BytecodeCompiler.cpp \
 		BytecodeEmitter.cpp \
 		CharacterEncoding.cpp \
 		FoldConstants.cpp \
 		Intl.cpp \
 		NameFunctions.cpp \
+		ParallelDo.cpp \
 		ParallelArray.cpp \
 		ParseMaps.cpp \
 		ParseNode.cpp \
 		Parser.cpp \
 		SPSProfiler.cpp \
 		SelfHosting.cpp \
 		TokenStream.cpp \
 		TestingFunctions.cpp \
@@ -312,17 +313,19 @@ CPPSRCS +=	MIR.cpp \
 		Snapshots.cpp \
 		Safepoints.cpp \
 		StupidAllocator.cpp \
 		TypeOracle.cpp \
 		TypePolicy.cpp \
 		ValueNumbering.cpp \
 		RangeAnalysis.cpp \
 		VMFunctions.cpp \
+		ParallelFunctions.cpp \
 		AliasAnalysis.cpp \
+		ParallelArrayAnalysis.cpp \
 		UnreachableCodeElimination.cpp \
 		$(NULL)
 endif #ENABLE_ION
 ifeq (86, $(findstring 86,$(TARGET_CPU)))
 ifdef ENABLE_ION
 CPPSRCS +=	CodeGenerator-x86-shared.cpp
 CPPSRCS +=	IonFrames-x86-shared.cpp
 CPPSRCS +=	MoveEmitter-x86-shared.cpp
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -243,8 +243,306 @@ function IsStructurallyValidLanguageTag(
     var pos = callFunction(std_String_indexOf, locale, "-x-");
     if (pos !== -1)
         locale = callFunction(std_String_substring, locale, 0, pos);
 
     // Check for duplicate variant or singleton subtags.
     return !callFunction(std_RegExp_test, duplicateVariantRE, locale) &&
            !callFunction(std_RegExp_test, duplicateSingletonRE, locale);
 }
+
+
+/**
+ * Canonicalizes the given structurally valid BCP 47 language tag, including
+ * regularized case of subtags. For example, the language tag
+ * Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE, where
+ *
+ *     Zh             ; 2*3ALPHA
+ *     -NAN           ; ["-" extlang]
+ *     -haNS          ; ["-" script]
+ *     -bu            ; ["-" region]
+ *     -variant2      ; *("-" variant)
+ *     -Variant1
+ *     -u-ca-chinese  ; *("-" extension)
+ *     -t-Zh-laTN
+ *     -x-PRIVATE     ; ["-" privateuse]
+ *
+ * becomes nan-Hans-mm-variant2-variant1-t-zh-latn-u-ca-chinese-x-private
+ *
+ * Spec: ECMAScript Internationalization API Specification, 6.2.3.
+ * Spec: RFC 5646, section 4.5.
+ */
+function CanonicalizeLanguageTag(locale) {
+    assert(IsStructurallyValidLanguageTag(locale), "CanonicalizeLanguageTag");
+
+    // The input
+    // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE"
+    // will be used throughout this method to illustrate how it works.
+
+    // Language tags are compared and processed case-insensitively, so
+    // technically it's not necessary to adjust case. But for easier processing,
+    // and because the canonical form for most subtags is lower case, we start
+    // with lower case for all.
+    // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE" ->
+    // "zh-nan-hans-bu-variant2-variant1-u-ca-chinese-t-zh-latn-x-private"
+    locale = callFunction(std_String_toLowerCase, locale);
+
+    // Handle mappings for complete tags.
+    if (callFunction(std_Object_hasOwnProperty, langTagMappings, locale))
+        return langTagMappings[locale];
+
+    var subtags = callFunction(std_String_split, locale, "-");
+    var i = 0;
+
+    // Handle the standard part: All subtags before the first singleton or "x".
+    // "zh-nan-hans-bu-variant2-variant1"
+    while (i < subtags.length) {
+        var subtag = subtags[i];
+
+        // If we reach the start of an extension sequence or private use part,
+        // we're done with this loop. We have to check for i > 0 because for
+        // irregular language tags, such as i-klingon, the single-character
+        // subtag "i" is not the start of an extension sequence.
+        // In the example, we break at "u".
+        if (subtag.length === 1 && (i > 0 || subtag === "x"))
+            break;
+
+        if (subtag.length === 4) {
+            // 4-character subtags are script codes; their first character
+            // needs to be capitalized. "hans" -> "Hans"
+            subtag = callFunction(std_String_toUpperCase, subtag[0]) +
+                     callFunction(std_String_substring, subtag, 1);
+        } else if (i !== 0 && subtag.length === 2) {
+            // 2-character subtags that are not in initial position are region
+            // codes; they need to be upper case. "bu" -> "BU"
+            subtag = callFunction(std_String_toUpperCase, subtag);
+        }
+        if (callFunction(std_Object_hasOwnProperty, langSubtagMappings, subtag)) {
+            // Replace deprecated subtags with their preferred values.
+            // "BU" -> "MM"
+            // This has to come after we capitalize region codes because
+            // otherwise some language and region codes could be confused.
+            // For example, "in" is an obsolete language code for Indonesian,
+            // but "IN" is the country code for India.
+            // Note that the script generating langSubtagMappings makes sure
+            // that no regular subtag mapping will replace an extlang code.
+            subtag = langSubtagMappings[subtag];
+        } else if (callFunction(std_Object_hasOwnProperty, extlangMappings, subtag)) {
+            // Replace deprecated extlang subtags with their preferred values,
+            // and remove the preceding subtag if it's a redundant prefix.
+            // "zh-nan" -> "nan"
+            // Note that the script generating extlangMappings makes sure that
+            // no extlang mapping will replace a normal language code.
+            subtag = extlangMappings[subtag].preferred;
+            if (i === 1 && extlangMappings[subtag].prefix === subtags[0]) {
+                callFunction(std_Array_shift, subtags);
+                i--;
+            }
+        }
+        subtags[i] = subtag;
+        i++;
+    }
+    var normal = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, 0, i), "-");
+
+    // Extension sequences are sorted by their singleton characters.
+    // "u-ca-chinese-t-zh-latn" -> "t-zh-latn-u-ca-chinese"
+    var extensions = new List();
+    while (i < subtags.length && subtags[i] !== "x") {
+        var extensionStart = i;
+        i++;
+        while (i < subtags.length && subtags[i].length > 1)
+            i++;
+        var extension = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, extensionStart, i), "-");
+        extensions.push(extension);
+    }
+    extensions.sort();
+
+    // Private use sequences are left as is. "x-private"
+    var privateUse = "";
+    if (i < subtags.length)
+        privateUse = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, i), "-");
+
+    // Put everything back together.
+    var canonical = normal;
+    if (extensions.length > 0)
+        canonical += "-" + extensions.join("-");
+    if (privateUse.length > 0) {
+        // Be careful of a Language-Tag that is entirely privateuse.
+        if (canonical.length > 0)
+            canonical += "-" + privateUse;
+        else
+            canonical = privateUse;
+    }
+
+    return canonical;
+}
+
+
+/**
+ * Verifies that the given string is a well-formed ISO 4217 currency code.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 6.3.1.
+ */
+function IsWellFormedCurrencyCode(currency) {
+    var c = ToString(currency);
+    var normalized = toASCIIUpperCase(c);
+    if (normalized.length !== 3)
+        return false;
+    return !callFunction(std_RegExp_test, /[^A-Z]/, normalized);
+}
+
+
+/********** Locale and Parameter Negotiation **********/
+
+
+/**
+ * Add old-style language tags without script code for locales that in current
+ * usage would include a script subtag. Returns the availableLocales argument
+ * provided.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 9.1.
+ */
+function addOldStyleLanguageTags(availableLocales) {
+    // checking for commonly used old-style language tags only
+    if (availableLocales["pa-Arab-PK"])
+        availableLocales["pa-PK"] = true;
+    if (availableLocales["zh-Hans-CN"])
+        availableLocales["zh-CN"] = true;
+    if (availableLocales["zh-Hans-SG"])
+        availableLocales["zh-SG"] = true;
+    if (availableLocales["zh-Hant-HK"])
+        availableLocales["zh-HK"] = true;
+    if (availableLocales["zh-Hant-TW"])
+        availableLocales["zh-TW"] = true;
+    return availableLocales;
+}
+
+
+/**
+ * Canonicalizes a locale list.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 9.2.1.
+ */
+function CanonicalizeLocaleList(locales) {
+    if (locales === undefined)
+        return new List();
+    var seen = new List();
+    if (typeof locales === "string")
+        locales = [locales];
+    var O = ToObject(locales);
+    var len = TO_UINT32(O.length);
+    var k = 0;
+    while (k < len) {
+        // Don't call ToString(k) - SpiderMonkey is faster with integers.
+        var kPresent = HasProperty(O, k);
+        if (kPresent) {
+            var kValue = O[k];
+            if (!(typeof kValue === "string" ||
+                  (typeof kValue === "object" && kValue !== null) ||
+                  // The following is here only because Waldo thinks we really
+                  // have to have it in order to be spec-conformant:
+                  // document.all is an object first implemented in Explorer
+                  // and then in Firefox and other fine browsers, whose
+                  // presence is also used by applications to identify Explorer
+                  // and which therefore has to be falsy in non-Explorer
+                  // browsers. It cloaks itself by pretending its type is
+                  // undefined. Just in case somebody thinks of decorating it
+                  // with a toString method that returns a language tag and
+                  // then passes it in as a locale, we check for its cloak
+                  // here.
+                  (typeof kValue === "undefined" && kValue !== undefined)))
+            {
+                ThrowError(JSMSG_INVALID_LOCALES_ELEMENT);
+            }
+            var tag = ToString(kValue);
+            if (!IsStructurallyValidLanguageTag(tag))
+                ThrowError(JSMSG_INVALID_LANGUAGE_TAG, tag);
+            tag = CanonicalizeLanguageTag(tag);
+            if (seen.indexOf(tag) === -1)
+                seen.push(tag);
+        }
+        k++;
+    }
+    return seen;
+}
+
+
+/**
+ * Compares a BCP 47 language tag against the locales in availableLocales
+ * and returns the best available match. Uses the fallback
+ * mechanism of RFC 4647, section 3.4.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 9.2.2.
+ * Spec: RFC 4647, section 3.4.
+ */
+function BestAvailableLocale(availableLocales, locale) {
+    assert(IsStructurallyValidLanguageTag(locale), "BestAvailableLocale");
+    assert(locale === CanonicalizeLanguageTag(locale), "BestAvailableLocale");
+    assert(callFunction(std_String_indexOf, locale, "-u-") === -1, "BestAvailableLocale");
+
+    var candidate = locale;
+    while (true) {
+        if (availableLocales[candidate])
+            return candidate;
+        var pos = callFunction(std_String_lastIndexOf, candidate, "-");
+        if (pos === -1)
+            return undefined;
+        if (pos >= 2 && candidate[pos - 2] === "-")
+            pos -= 2;
+        candidate = callFunction(std_String_substring, candidate, 0, pos);
+    }
+}
+
+
+/**
+ * Compares a BCP 47 language priority list against the set of locales in
+ * availableLocales and determines the best available language to meet the
+ * request. Options specified through Unicode extension subsequences are
+ * ignored in the lookup, but information about such subsequences is returned
+ * separately.
+ *
+ * This variant is based on the Lookup algorithm of RFC 4647 section 3.4.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 9.2.3.
+ * Spec: RFC 4647, section 3.4.
+ */
+function LookupMatcher(availableLocales, requestedLocales) {
+    var i = 0;
+    var len = requestedLocales.length;
+    var availableLocale;
+    var locale, noExtensionsLocale;
+    while (i < len && availableLocale === undefined) {
+        locale = requestedLocales[i];
+        noExtensionsLocale = callFunction(std_String_replace, locale, unicodeLocaleExtensionSequenceGlobalRE, "");
+        availableLocale = BestAvailableLocale(availableLocales, noExtensionsLocale);
+        i++;
+    }
+
+    var result = new Record();
+    if (availableLocale !== undefined) {
+        result.__locale = availableLocale;
+        if (locale !== noExtensionsLocale) {
+            var extensionMatch = callFunction(std_String_match, locale, unicodeLocaleExtensionSequenceRE);
+            var extension = extensionMatch[0];
+            var extensionIndex = extensionMatch.index;
+            result.__extension = extension;
+            result.__extensionIndex = extensionIndex;
+        }
+    } else {
+        result.__locale = DefaultLocale();
+    }
+    return result;
+}
+
+
+/**
+ * Compares a BCP 47 language priority list against the set of locales in
+ * availableLocales and determines the best available language to meet the
+ * request. Options specified through Unicode extension subsequences are
+ * ignored in the lookup, but information about such subsequences is returned
+ * separately.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 9.2.4.
+ */
+function BestFitMatcher(availableLocales, requestedLocales) {
+    // this implementation doesn't have anything better
+    return LookupMatcher(availableLocales, requestedLocales);
+}
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -11,16 +11,17 @@
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsobjinlines.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 
 #include "builtin/TestingFunctions.h"
 #include "methodjit/MethodJIT.h"
+#include "vm/ForkJoin.h"
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace JS;
 
 using mozilla::ArrayLength;
 
@@ -873,16 +874,26 @@ DisplayName(JSContext *cx, unsigned argc
     }
 
     JSFunction *fun = args[0].toObject().toFunction();
     JSString *str = fun->displayAtom();
     vp->setString(str == NULL ? cx->runtime->emptyString : str);
     return true;
 }
 
+JSBool
+js::testingFunc_inParallelSection(JSContext *cx, unsigned argc, jsval *vp)
+{
+    // If we were actually *in* a parallel section, then this function
+    // would be inlined to TRUE in ion-generated code.
+    JS_ASSERT(!InParallelSection());
+    JS_SET_RVAL(cx, vp, JSVAL_FALSE);
+    return true;
+}
+
 static JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment')",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc."),
 
     JS_FN_HELP("gcparam", GCParameter, 2, 0,
@@ -1004,16 +1015,20 @@ static JSFunctionSpecWithHelp TestingFun
 "  assertions are disabled."),
 
     JS_FN_HELP("displayName", DisplayName, 1, 0,
 "displayName(fn)",
 "  Gets the display name for a function, which can possibly be a guessed or\n"
 "  inferred name based on where the function was defined. This can be\n"
 "  different from the 'name' property on the function."),
 
+    JS_FN_HELP("inParallelSection", testingFunc_inParallelSection, 0, 0,
+"inParallelSection()",
+"  True if this code is executing within a parallel section."),
+
     JS_FS_HELP_END
 };
 
 bool
 js::DefineTestingFunctions(JSContext *cx, HandleObject obj)
 {
     return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
 }
--- a/js/src/builtin/TestingFunctions.h
+++ b/js/src/builtin/TestingFunctions.h
@@ -6,11 +6,14 @@
 #ifndef TestingFunctions_h__
 #define TestingFunctions_h__
 
 namespace js {
 
 bool
 DefineTestingFunctions(JSContext *cx, JSHandleObject obj);
 
+JSBool
+testingFunc_inParallelSection(JSContext *cx, unsigned argc, jsval *vp);
+
 } /* namespace js */
 
 #endif /* TestingFunctions_h__ */
--- a/js/src/ds/LifoAlloc.cpp
+++ b/js/src/ds/LifoAlloc.cpp
@@ -29,19 +29,27 @@ BumpChunk::new_(size_t chunkSize)
     JS_ASSERT(AlignPtr(result->bump) == result->bump);
     return result;
 }
 
 void
 BumpChunk::delete_(BumpChunk *chunk)
 {
 #ifdef DEBUG
-        memset(chunk, 0xcd, sizeof(*chunk) + chunk->bumpSpaceSize);
+    // Part of the chunk may have been marked as poisoned/noaccess.  Undo that
+    // before writing the 0xcd bytes.
+    size_t size = sizeof(*chunk) + chunk->bumpSpaceSize;
+#if defined(MOZ_ASAN)
+    ASAN_UNPOISON_MEMORY_REGION(chunk, size);
+#elif defined(MOZ_VALGRIND)
+    VALGRIND_MAKE_MEM_UNDEFINED(chunk, size);
 #endif
-        js_free(chunk);
+    memset(chunk, 0xcd, size);
+#endif
+    js_free(chunk);
 }
 
 bool
 BumpChunk::canAlloc(size_t n)
 {
     char *aligned = AlignPtr(bump);
     char *bumped = aligned + n;
     return bumped <= limit && bumped > headerBase();
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -393,17 +393,17 @@ struct CompileError {
     }
     ~CompileError();
     void throwError();
 };
 
 inline bool
 StrictModeFromContext(JSContext *cx)
 {
-    return cx->hasRunOption(JSOPTION_STRICT_MODE);
+    return cx->hasOption(JSOPTION_STRICT_MODE);
 }
 
 // Ideally, tokenizing would be entirely independent of context.  But the
 // strict mode flag, which is in SharedContext, affects tokenizing, and
 // TokenStream needs to see it.
 //
 // This class is a tiny back-channel from TokenStream to the strict mode flag
 // that avoids exposing the rest of SharedContext to TokenStream. get()
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -605,16 +605,23 @@ class Rooted : public RootedBase<T>
 {
     void init(JSContext *cxArg) {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         ContextFriendFields *cx = ContextFriendFields::get(cxArg);
         commonInit(cx->thingGCRooters);
 #endif
     }
 
+    void init(PerThreadData *ptArg) {
+#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
+        PerThreadDataFriendFields *pt = PerThreadDataFriendFields::get(ptArg);
+        commonInit(pt->thingGCRooters);
+#endif
+    }
+
   public:
     Rooted(JSContext *cx
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(RootMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
@@ -631,16 +638,41 @@ class Rooted : public RootedBase<T>
     Rooted(JSContext *cx, const Unrooted<S> &initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(static_cast<S>(initial))
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
 
+    Rooted(PerThreadData *pt
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : ptr(RootMethods<T>::initial())
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        init(pt);
+    }
+
+    Rooted(PerThreadData *pt, T initial
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : ptr(initial)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        init(pt);
+    }
+
+    template <typename S>
+    Rooted(PerThreadData *pt, const Unrooted<S> &initial
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : ptr(static_cast<S>(initial))
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        init(pt);
+    }
+
     ~Rooted() {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         JS_ASSERT(*stack == this);
         *stack = prev;
 #endif
     }
 
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -75,16 +75,18 @@ MarkExactStackRootList(JSTracer *trc, Ro
 }
 
 static void
 MarkExactStackRoots(JSTracer *trc)
 {
     for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
         for (ContextIter cx(trc->runtime); !cx.done(); cx.next())
             MarkExactStackRootList(trc, cx->thingGCRooters[i], ThingRootKind(i));
+
+        MarkExactStackRootList(trc, trc->runtime->mainThread->thingGCRooters[i], ThingRootKind(i));
     }
 }
 #endif /* JSGC_USE_EXACT_ROOTING */
 
 enum ConservativeGCTest
 {
     CGCT_VALID,
     CGCT_LOWBITSET, /* excluded because one of the low bits was set */
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -191,16 +191,29 @@ SuppressCheckRoots(Vector<Rooter, 0, Sys
     stacks[oldestMemory] = hash;
     oldestMemory = (oldestMemory + 1) % NumStackMemories;
     if (numMemories < NumStackMemories)
         numMemories++;
 
     return false;
 }
 
+static void
+GatherRooters(Vector<Rooter, 0, SystemAllocPolicy> &rooters,
+              Rooted<void*> **thingGCRooters,
+              unsigned thingRootKind)
+{
+    Rooted<void*> *rooter = thingGCRooters[thingRootKind];
+    while (rooter) {
+        Rooter r = { rooter, ThingRootKind(thingRootKind) };
+        JS_ALWAYS_TRUE(rooters.append(r));
+        rooter = rooter->previous();
+    }
+}
+
 void
 JS::CheckStackRoots(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
 
     if (rt->gcZeal_ != ZealStackRootingValue)
         return;
 
@@ -238,26 +251,23 @@ JS::CheckStackRoots(JSContext *cx)
     stackMin = rt->nativeStackBase;
     stackEnd = cgcd->nativeStackTop;
 #else
     stackMin = cgcd->nativeStackTop + 1;
     stackEnd = reinterpret_cast<uintptr_t *>(rt->nativeStackBase);
 #endif
 
     // Gather up all of the rooters
-    Vector< Rooter, 0, SystemAllocPolicy> rooters;
+    Vector<Rooter, 0, SystemAllocPolicy> rooters;
     for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
         for (ContextIter cx(rt); !cx.done(); cx.next()) {
-            Rooted<void*> *rooter = cx->thingGCRooters[i];
-            while (rooter) {
-                Rooter r = { rooter, ThingRootKind(i) };
-                JS_ALWAYS_TRUE(rooters.append(r));
-                rooter = rooter->previous();
-            }
+            GatherRooters(rooters, cx->thingGCRooters, i);
         }
+
+        GatherRooters(rooters, rt->mainThread.thingGCRooters, i);
     }
 
     if (SuppressCheckRoots(rooters))
         return;
 
     // Truncate stackEnd to just after the address of the youngest
     // already-scanned rooter on the stack, to avoid re-scanning the rest of
     // the stack.
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -234,16 +234,18 @@ ConvertFrames(JSContext *cx, IonActivati
     AutoAssertNoGC nogc;
     IonSpew(IonSpew_Bailouts, "Bailing out %s:%u, IonScript %p",
             it.script()->filename, it.script()->lineno, (void *) it.ionScript());
     IonSpew(IonSpew_Bailouts, " reading from snapshot offset %u size %u",
             it.snapshotOffset(), it.ionScript()->snapshotsSize());
 #ifdef DEBUG
     // Use count is reset after invalidation. Log use count on bailouts to
     // determine if we have a critical sequence of bailout.
+    //
+    // Note: frame conversion only occurs in sequential mode
     if (it.script()->ion == it.ionScript()) {
         IonSpew(IonSpew_Bailouts, " Current script use count is %u",
                 it.script()->getUseCount());
     }
 #endif
 
     SnapshotIterator iter(it);
 
@@ -302,16 +304,18 @@ ConvertFrames(JSContext *cx, IonActivati
              break;
         iter.nextFrame();
 
         fp = PushInlinedFrame(cx, fp);
         if (!fp)
             return BAILOUT_RETURN_OVERRECURSED;
     }
 
+    fp->clearRunningInIon();
+
     jsbytecode *bailoutPc = fp->script()->code + iter.pcOffset();
     br->setBailoutPc(bailoutPc);
 
     switch (iter.bailoutKind()) {
       case Bailout_Normal:
         return BAILOUT_RETURN_OK;
       case Bailout_TypeBarrier:
         return BAILOUT_RETURN_TYPE_BARRIER;
@@ -612,17 +616,16 @@ ion::ThunkToInterpreter(Value *vp)
         cx->regs().pc = GetNextPc(pc);
 
     // When JSScript::argumentsOptimizationFailed, we cannot access Ion frames
     // in order to create an arguments object for them.  However, there is an
     // invariant that script->needsArgsObj() implies fp->hasArgsObj() (after the
     // prologue), so we must create one now for each inlined frame which needs
     // one.
     {
-        br->entryfp()->clearRunningInIon();
         ScriptFrameIter iter(cx);
         StackFrame *fp = NULL;
         Rooted<JSScript*> script(cx);
         do {
             fp = iter.interpFrame();
             script = iter.script();
             if (script->needsArgsObj()) {
                 // Currently IonMonkey does not compile if the script needs an
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -13,17 +13,19 @@
 #include "CodeGenerator.h"
 #include "IonLinker.h"
 #include "IonSpewer.h"
 #include "MIRGenerator.h"
 #include "shared/CodeGenerator-shared-inl.h"
 #include "jsnum.h"
 #include "jsmath.h"
 #include "jsinterpinlines.h"
+#include "ParallelFunctions.h"
 #include "ExecutionModeInlines.h"
+#include "vm/ForkJoin.h"
 
 #include "vm/StringObject-inl.h"
 
 using namespace js;
 using namespace js::ion;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
@@ -471,16 +473,27 @@ CodeGenerator::visitLambda(LLambda *lir)
         return false;
 
     JS_ASSERT(gen->compartment == fun->compartment());
     JS_ASSERT(!fun->hasSingletonType());
 
     masm.newGCThing(output, fun, ool->entry());
     masm.initGCThing(output, fun);
 
+    emitLambdaInit(output, scopeChain, fun);
+
+    masm.bind(ool->rejoin());
+    return true;
+}
+
+void
+CodeGenerator::emitLambdaInit(const Register &output,
+                              const Register &scopeChain,
+                              JSFunction *fun)
+{
     // Initialize nargs and flags. We do this with a single uint32 to avoid
     // 16-bit writes.
     union {
         struct S {
             uint16_t nargs;
             uint16_t flags;
         } s;
         uint32_t word;
@@ -489,18 +502,32 @@ CodeGenerator::visitLambda(LLambda *lir)
     u.s.flags = fun->flags & ~JSFunction::EXTENDED;
 
     JS_STATIC_ASSERT(offsetof(JSFunction, flags) == offsetof(JSFunction, nargs) + 2);
     masm.store32(Imm32(u.word), Address(output, offsetof(JSFunction, nargs)));
     masm.storePtr(ImmGCPtr(fun->nonLazyScript()),
                   Address(output, JSFunction::offsetOfNativeOrScript()));
     masm.storePtr(scopeChain, Address(output, JSFunction::offsetOfEnvironment()));
     masm.storePtr(ImmGCPtr(fun->displayAtom()), Address(output, JSFunction::offsetOfAtom()));
-
-    masm.bind(ool->rejoin());
+}
+
+bool
+CodeGenerator::visitParLambda(LParLambda *lir)
+{
+    Register resultReg = ToRegister(lir->output());
+    Register parSliceReg = ToRegister(lir->parSlice());
+    Register scopeChainReg    = ToRegister(lir->scopeChain());
+    Register tempReg1 = ToRegister(lir->getTemp0());
+    Register tempReg2 = ToRegister(lir->getTemp1());
+    JSFunction *fun = lir->mir()->fun();
+
+    JS_ASSERT(scopeChainReg != resultReg);
+
+    emitParAllocateGCThing(resultReg, parSliceReg, tempReg1, tempReg2, fun);
+    emitLambdaInit(resultReg, scopeChainReg, fun);
     return true;
 }
 
 bool
 CodeGenerator::visitLabel(LLabel *lir)
 {
     masm.bind(lir->label());
     return true;
@@ -759,16 +786,62 @@ bool
 CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment *lir)
 {
     Address environment(ToRegister(lir->function()), JSFunction::offsetOfEnvironment());
     masm.loadPtr(environment, ToRegister(lir->output()));
     return true;
 }
 
 bool
+CodeGenerator::visitParSlice(LParSlice *lir)
+{
+    const Register tempReg = ToRegister(lir->getTempReg());
+
+    masm.setupUnalignedABICall(0, tempReg);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParForkJoinSlice));
+    JS_ASSERT(ToRegister(lir->output()) == ReturnReg);
+    return true;
+}
+
+bool
+CodeGenerator::visitParWriteGuard(LParWriteGuard *lir)
+{
+    JS_ASSERT(gen->info().executionMode() == ParallelExecution);
+
+    const Register tempReg = ToRegister(lir->getTempReg());
+    masm.setupUnalignedABICall(2, tempReg);
+    masm.passABIArg(ToRegister(lir->parSlice()));
+    masm.passABIArg(ToRegister(lir->object()));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParWriteGuard));
+
+    Label *bail;
+    if (!ensureOutOfLineParallelAbort(&bail))
+        return false;
+
+    // branch to the OOL failure code if false is returned
+    masm.branchTestBool(Assembler::Zero, ReturnReg, ReturnReg, bail);
+
+    return true;
+}
+
+bool
+CodeGenerator::visitParDump(LParDump *lir)
+{
+    ValueOperand value = ToValue(lir, 0);
+    masm.reserveStack(sizeof(Value));
+    masm.storeValue(value, Address(StackPointer, 0));
+    masm.movePtr(StackPointer, CallTempReg0);
+    masm.setupUnalignedABICall(1, CallTempReg1);
+    masm.passABIArg(CallTempReg0);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParDumpValue));
+    masm.freeStack(sizeof(Value));
+    return true;
+}
+
+bool
 CodeGenerator::visitTypeBarrier(LTypeBarrier *lir)
 {
     ValueOperand operand = ToValue(lir, LTypeBarrier::Input);
     Register scratch = ToRegister(lir->temp());
 
     Label mismatched;
     masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &mismatched);
     if (!bailoutFrom(&mismatched, lir->snapshot()))
@@ -981,18 +1054,36 @@ CodeGenerator::visitCallDOMNative(LCallD
 
 typedef bool (*GetIntrinsicValueFn)(JSContext *cx, HandlePropertyName, MutableHandleValue);
 static const VMFunction GetIntrinsicValueInfo =
     FunctionInfo<GetIntrinsicValueFn>(GetIntrinsicValue);
 
 bool
 CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir)
 {
-    pushArg(ImmGCPtr(lir->mir()->name()));
-    return callVM(GetIntrinsicValueInfo, lir);
+    // When compiling parallel kernels, always bail.
+    switch (gen->info().executionMode()) {
+      case SequentialExecution: {
+        pushArg(ImmGCPtr(lir->mir()->name()));
+        return callVM(GetIntrinsicValueInfo, lir);
+      }
+
+      case ParallelExecution: {
+        Label *bail;
+        if (!ensureOutOfLineParallelAbort(&bail))
+            return false;
+
+        masm.jump(bail);
+        return true;
+      }
+
+      default:
+        JS_NOT_REACHED("Bad execution mode");
+        return false;
+    }
 }
 
 typedef bool (*InvokeFunctionFn)(JSContext *, HandleFunction, uint32_t, Value *, Value *);
 static const VMFunction InvokeFunctionInfo = FunctionInfo<InvokeFunctionFn>(InvokeFunction);
 
 bool
 CodeGenerator::emitCallInvokeFunction(LInstruction *call, Register calleereg,
                                       uint32_t argc, uint32_t unusedStack)
@@ -1026,17 +1117,18 @@ static inline int32_t ionOffset(Executio
 
 bool
 CodeGenerator::visitCallGeneric(LCallGeneric *call)
 {
     Register calleereg = ToRegister(call->getFunction());
     Register objreg    = ToRegister(call->getTempObject());
     Register nargsreg  = ToRegister(call->getNargsReg());
     uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
-    Label invoke, thunk, makeCall, end;
+    ExecutionMode executionMode = gen->info().executionMode();
+    Label uncompiled, thunk, makeCall, end;
 
     // Known-target case is handled by LCallKnown.
     JS_ASSERT(!call->hasSingleTarget());
 
     // Generate an ArgumentsRectifier.
     IonCompartment *ion = gen->ionCompartment();
     IonCode *argumentsRectifier = ion->getArgumentsRectifier();
 
@@ -1044,25 +1136,24 @@ CodeGenerator::visitCallGeneric(LCallGen
 
     // Guard that calleereg is actually a function object.
     masm.loadObjClass(calleereg, nargsreg);
     masm.cmpPtr(nargsreg, ImmWord(&js::FunctionClass));
     if (!bailoutIf(Assembler::NotEqual, call->snapshot()))
         return false;
 
     // Guard that calleereg is an interpreted function with a JSScript:
-    masm.branchIfFunctionHasNoScript(calleereg, &invoke);
+    masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
 
     // Knowing that calleereg is a non-native function, load the JSScript.
     masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
-    ExecutionMode executionMode = gen->info().executionMode();
     masm.loadPtr(Address(objreg, ionOffset(executionMode)), objreg);
 
     // Guard that the IonScript has been compiled.
-    masm.branchPtr(Assembler::BelowOrEqual, objreg, ImmWord(ION_COMPILING_SCRIPT), &invoke);
+    masm.branchPtr(Assembler::BelowOrEqual, objreg, ImmWord(ION_COMPILING_SCRIPT), &uncompiled);
 
     // Nestle the StackPointer up to the argument vector.
     masm.freeStack(unusedStack);
 
     // Construct the IonFramePrefix.
     uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), IonFrame_OptimizedJS);
     masm.Push(Imm32(call->numActualArgs()));
     masm.Push(calleereg);
@@ -1095,60 +1186,93 @@ CodeGenerator::visitCallGeneric(LCallGen
 
     // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
     // The return address has already been removed from the Ion frame.
     int prefixGarbage = sizeof(IonJSFrameLayout) - sizeof(void *);
     masm.adjustStack(prefixGarbage - unusedStack);
     masm.jump(&end);
 
     // Handle uncompiled or native functions.
-    masm.bind(&invoke);
-    if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
-        return false;
+    masm.bind(&uncompiled);
+    switch (executionMode) {
+      case SequentialExecution:
+        if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
+            return false;
+        break;
+
+      case ParallelExecution:
+        if (!emitParCallToUncompiledScript(calleereg))
+            return false;
+        break;
+    }
 
     masm.bind(&end);
 
     // If the return value of the constructing function is Primitive,
     // replace the return value with the Object from CreateThis.
     if (call->mir()->isConstructing()) {
         Label notPrimitive;
         masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
         masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
         masm.bind(&notPrimitive);
     }
 
+    if (!checkForParallelBailout())
+        return false;
+
     dropArguments(call->numStackArgs() + 1);
     return true;
 }
 
+// Generates a call to ParCallToUncompiledScript() and then bails out.
+// |calleeReg| should contain the JSFunction*.
+bool
+CodeGenerator::emitParCallToUncompiledScript(Register calleeReg)
+{
+    Label *bail;
+    if (!ensureOutOfLineParallelAbort(&bail))
+        return false;
+
+    masm.movePtr(calleeReg, CallTempReg0);
+    masm.setupUnalignedABICall(1, CallTempReg1);
+    masm.passABIArg(CallTempReg0);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParCallToUncompiledScript));
+    masm.jump(bail);
+    return true;
+}
+
 bool
 CodeGenerator::visitCallKnown(LCallKnown *call)
 {
     JSContext *cx = GetIonContext()->cx;
     Register calleereg = ToRegister(call->getFunction());
     Register objreg    = ToRegister(call->getTempObject());
     uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
     RootedFunction target(cx, call->getSingleTarget());
-    Label end, invoke;
+    ExecutionMode executionMode = gen->info().executionMode();
+    Label end, uncompiled;
 
     // Native single targets are handled by LCallNative.
     JS_ASSERT(!target->isNative());
     // Missing arguments must have been explicitly appended by the IonBuilder.
     JS_ASSERT(target->nargs <= call->numStackArgs());
 
     masm.checkStackAlignment();
 
     // Make sure the function has a JSScript
     if (target->isInterpretedLazy() && !target->getOrCreateScript(cx))
         return false;
 
-    // If the function is known to be uncompilable, only emit the call to InvokeFunction.
-    ExecutionMode executionMode = gen->info().executionMode();
+    // If the function is known to be uncompilable, just emit the call to
+    // Invoke in sequential mode, else mark as cannot compile.
     RootedScript targetScript(cx, target->nonLazyScript());
     if (GetIonScript(targetScript, executionMode) == ION_DISABLED_SCRIPT) {
+        if (executionMode == ParallelExecution)
+            return false;
+
         if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
             return false;
 
         if (call->mir()->isConstructing()) {
             Label notPrimitive;
             masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
             masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
             masm.bind(&notPrimitive);
@@ -1158,17 +1282,17 @@ CodeGenerator::visitCallKnown(LCallKnown
         return true;
     }
 
     // Knowing that calleereg is a non-native function, load the JSScript.
     masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
     masm.loadPtr(Address(objreg, ionOffset(executionMode)), objreg);
 
     // Guard that the IonScript has been compiled.
-    masm.branchPtr(Assembler::BelowOrEqual, objreg, ImmWord(ION_COMPILING_SCRIPT), &invoke);
+    masm.branchPtr(Assembler::BelowOrEqual, objreg, ImmWord(ION_COMPILING_SCRIPT), &uncompiled);
 
     // Load the start of the target IonCode.
     masm.loadPtr(Address(objreg, IonScript::offsetOfMethod()), objreg);
     masm.loadPtr(Address(objreg, IonCode::offsetOfCode()), objreg);
 
     // Nestle the StackPointer up to the argument vector.
     masm.freeStack(unusedStack);
 
@@ -1185,36 +1309,64 @@ CodeGenerator::visitCallKnown(LCallKnown
 
     // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
     // The return address has already been removed from the Ion frame.
     int prefixGarbage = sizeof(IonJSFrameLayout) - sizeof(void *);
     masm.adjustStack(prefixGarbage - unusedStack);
     masm.jump(&end);
 
     // Handle uncompiled functions.
-    masm.bind(&invoke);
-    if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
-        return false;
+    masm.bind(&uncompiled);
+    switch (executionMode) {
+      case SequentialExecution:
+        if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
+            return false;
+        break;
+
+      case ParallelExecution:
+        if (!emitParCallToUncompiledScript(calleereg))
+            return false;
+        break;
+    }
 
     masm.bind(&end);
 
+    if (!checkForParallelBailout())
+        return false;
+
     // If the return value of the constructing function is Primitive,
     // replace the return value with the Object from CreateThis.
     if (call->mir()->isConstructing()) {
         Label notPrimitive;
         masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
         masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
         masm.bind(&notPrimitive);
     }
 
     dropArguments(call->numStackArgs() + 1);
     return true;
 }
 
 bool
+CodeGenerator::checkForParallelBailout()
+{
+    // In parallel mode, if we call another ion-compiled function and
+    // it returns JS_ION_ERROR, that indicates a bailout that we have
+    // to propagate up the stack.
+    ExecutionMode executionMode = gen->info().executionMode();
+    if (executionMode == ParallelExecution) {
+        Label *bail;
+        if (!ensureOutOfLineParallelAbort(&bail))
+            return false;
+        masm.branchTestMagic(Assembler::Equal, JSReturnOperand, bail);
+    }
+    return true;
+}
+
+bool
 CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize)
 {
     Register objreg = ToRegister(apply->getTempObject());
     JS_ASSERT(objreg != extraStackSize);
 
     // Push the space used by the arguments.
     masm.movePtr(StackPointer, objreg);
     masm.Push(extraStackSize);
@@ -1574,16 +1726,152 @@ CodeGenerator::visitCheckOverRecursedFai
     if (!callVM(CheckOverRecursedInfo, ool->lir()))
         return false;
 
     restoreLive(ool->lir());
     masm.jump(ool->rejoin());
     return true;
 }
 
+// Out-of-line path to report over-recursed error and fail.
+class ParCheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator>
+{
+    LParCheckOverRecursed *lir_;
+
+  public:
+    ParCheckOverRecursedFailure(LParCheckOverRecursed *lir)
+      : lir_(lir)
+    { }
+
+    bool accept(CodeGenerator *codegen) {
+        return codegen->visitParCheckOverRecursedFailure(this);
+    }
+
+    LParCheckOverRecursed *lir() const {
+        return lir_;
+    }
+};
+
+bool
+CodeGenerator::visitParCheckOverRecursed(LParCheckOverRecursed *lir)
+{
+    // See above: unlike visitCheckOverRecursed(), this code runs in
+    // parallel mode and hence uses the ionStackLimit from the current
+    // thread state.  Also, we must check the interrupt flags because
+    // on interrupt or abort, only the stack limit for the main thread
+    // is reset, not the worker threads.  See comment in vm/ForkJoin.h
+    // for more details.
+
+    Register parSliceReg = ToRegister(lir->parSlice());
+    Register tempReg = ToRegister(lir->getTempReg());
+
+    masm.loadPtr(Address(parSliceReg, offsetof(ForkJoinSlice, perThreadData)), tempReg);
+    masm.loadPtr(Address(tempReg, offsetof(PerThreadData, ionStackLimit)), tempReg);
+
+    // Conditional forward (unlikely) branch to failure.
+    ParCheckOverRecursedFailure *ool = new ParCheckOverRecursedFailure(lir);
+    if (!addOutOfLineCode(ool))
+        return false;
+    masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry());
+    masm.parCheckInterruptFlags(tempReg, ool->entry());
+    masm.bind(ool->rejoin());
+
+    return true;
+}
+
+bool
+CodeGenerator::visitParCheckOverRecursedFailure(ParCheckOverRecursedFailure *ool)
+{
+    Label *bail;
+    if (!ensureOutOfLineParallelAbort(&bail))
+        return false;
+
+    // Avoid saving/restoring the temp register since we will put the
+    // ReturnReg into it below and we don't want to clobber that
+    // during PopRegsInMask():
+    LParCheckOverRecursed *lir = ool->lir();
+    Register tempReg = ToRegister(lir->getTempReg());
+    RegisterSet saveSet(lir->safepoint()->liveRegs());
+    saveSet.maybeTake(tempReg);
+
+    masm.PushRegsInMask(saveSet);
+    masm.movePtr(ToRegister(lir->parSlice()), CallTempReg0);
+    masm.setupUnalignedABICall(1, CallTempReg1);
+    masm.passABIArg(CallTempReg0);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParCheckOverRecursed));
+    masm.movePtr(ReturnReg, tempReg);
+    masm.PopRegsInMask(saveSet);
+    masm.branchTestBool(Assembler::Zero, tempReg, tempReg, bail);
+    masm.jump(ool->rejoin());
+
+    return true;
+}
+
+// Out-of-line path to report over-recursed error and fail.
+class OutOfLineParCheckInterrupt : public OutOfLineCodeBase<CodeGenerator>
+{
+  public:
+    LParCheckInterrupt *const lir;
+
+    OutOfLineParCheckInterrupt(LParCheckInterrupt *lir)
+      : lir(lir)
+    { }
+
+    bool accept(CodeGenerator *codegen) {
+        return codegen->visitOutOfLineParCheckInterrupt(this);
+    }
+};
+
+bool
+CodeGenerator::visitParCheckInterrupt(LParCheckInterrupt *lir)
+{
+    // First check for slice->shared->interrupt_.
+    OutOfLineParCheckInterrupt *ool = new OutOfLineParCheckInterrupt(lir);
+    if (!addOutOfLineCode(ool))
+        return false;
+
+    // We must check two flags:
+    // - runtime->interrupt
+    // - runtime->parallelAbort
+    // See vm/ForkJoin.h for discussion on why we use this design.
+
+    Register tempReg = ToRegister(lir->getTempReg());
+    masm.parCheckInterruptFlags(tempReg, ool->entry());
+    masm.bind(ool->rejoin());
+    return true;
+}
+
+bool
+CodeGenerator::visitOutOfLineParCheckInterrupt(OutOfLineParCheckInterrupt *ool)
+{
+    Label *bail;
+    if (!ensureOutOfLineParallelAbort(&bail))
+        return false;
+
+    // Avoid saving/restoring the temp register since we will put the
+    // ReturnReg into it below and we don't want to clobber that
+    // during PopRegsInMask():
+    LParCheckInterrupt *lir = ool->lir;
+    Register tempReg = ToRegister(lir->getTempReg());
+    RegisterSet saveSet(lir->safepoint()->liveRegs());
+    saveSet.maybeTake(tempReg);
+
+    masm.PushRegsInMask(saveSet);
+    masm.movePtr(ToRegister(ool->lir->parSlice()), CallTempReg0);
+    masm.setupUnalignedABICall(1, CallTempReg1);
+    masm.passABIArg(CallTempReg0);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParCheckInterrupt));
+    masm.movePtr(ReturnReg, tempReg);
+    masm.PopRegsInMask(saveSet);
+    masm.branchTestBool(Assembler::Zero, tempReg, tempReg, bail);
+    masm.jump(ool->rejoin());
+
+    return true;
+}
+
 IonScriptCounts *
 CodeGenerator::maybeCreateScriptCounts()
 {
     // If scripts are being profiled, create a new IonScriptCounts and attach
     // it to the script. This must be done on the main thread.
     JSContext *cx = GetIonContext()->cx;
     if (!cx)
         return NULL;
@@ -1723,16 +2011,19 @@ CodeGenerator::generateBody()
             if (counts)
                 blockCounts.ref().visitInstruction(*iter);
 
             if (iter->safepoint() && pushedArgumentSlots_.length()) {
                 if (!markArgumentSlots(iter->safepoint()))
                     return false;
             }
 
+            if (!callTraceLIR(i, *iter))
+                return false;
+
             if (!iter->accept(this))
                 return false;
         }
         if (masm.oom())
             return false;
     }
 
     JS_ASSERT(pushedArgumentSlots_.empty());
@@ -1760,16 +2051,18 @@ class OutOfLineNewArray : public OutOfLi
 
 typedef JSObject *(*NewInitArrayFn)(JSContext *, uint32_t, types::TypeObject *);
 static const VMFunction NewInitArrayInfo =
     FunctionInfo<NewInitArrayFn>(NewInitArray);
 
 bool
 CodeGenerator::visitNewArrayCallVM(LNewArray *lir)
 {
+    JS_ASSERT(gen->info().executionMode() == SequentialExecution);
+
     Register objReg = ToRegister(lir->output());
 
     JS_ASSERT(!lir->isCall());
     saveLive(lir);
 
     JSObject *templateObject = lir->mir()->templateObject();
     types::TypeObject *type = templateObject->hasSingletonType() ? NULL : templateObject->type();
 
@@ -1808,31 +2101,24 @@ CodeGenerator::visitNewSlots(LNewSlots *
         return false;
 
     return true;
 }
 
 bool
 CodeGenerator::visitNewArray(LNewArray *lir)
 {
+    JS_ASSERT(gen->info().executionMode() == SequentialExecution);
     Register objReg = ToRegister(lir->output());
     JSObject *templateObject = lir->mir()->templateObject();
     uint32_t count = lir->mir()->count();
 
     JS_ASSERT(count < JSObject::NELEMENTS_LIMIT);
 
-    size_t maxArraySlots =
-        gc::GetGCKindSlots(gc::FINALIZE_OBJECT_LAST) - ObjectElements::VALUES_PER_HEADER;
-
-    // Allocate space using the VMCall
-    // when mir hints it needs to get allocated immediatly,
-    // but only when data doesn't fit the available array slots.
-    bool allocating = lir->mir()->isAllocating() && count > maxArraySlots;
-
-    if (templateObject->hasSingletonType() || allocating)
+    if (lir->mir()->shouldUseVM())
         return visitNewArrayCallVM(lir);
 
     OutOfLineNewArray *ool = new OutOfLineNewArray(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
     masm.newGCThing(objReg, templateObject, ool->entry());
     masm.initGCThing(objReg, templateObject);
@@ -1870,16 +2156,18 @@ class OutOfLineNewObject : public OutOfL
 };
 
 typedef JSObject *(*NewInitObjectFn)(JSContext *, HandleObject);
 static const VMFunction NewInitObjectInfo = FunctionInfo<NewInitObjectFn>(NewInitObject);
 
 bool
 CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
 {
+    JS_ASSERT(gen->info().executionMode() == SequentialExecution);
+
     Register objReg = ToRegister(lir->output());
 
     JS_ASSERT(!lir->isCall());
     saveLive(lir);
 
     pushArg(ImmGCPtr(lir->mir()->templateObject()));
     if (!callVM(NewInitObjectInfo, lir))
         return false;
@@ -1889,21 +2177,21 @@ CodeGenerator::visitNewObjectVMCall(LNew
 
     restoreLive(lir);
     return true;
 }
 
 bool
 CodeGenerator::visitNewObject(LNewObject *lir)
 {
+    JS_ASSERT(gen->info().executionMode() == SequentialExecution);
     Register objReg = ToRegister(lir->output());
-
     JSObject *templateObject = lir->mir()->templateObject();
 
-    if (templateObject->hasSingletonType() || templateObject->hasDynamicSlots())
+    if (lir->mir()->shouldUseVM())
         return visitNewObjectVMCall(lir);
 
     OutOfLineNewObject *ool = new OutOfLineNewObject(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
     masm.newGCThing(objReg, templateObject, ool->entry());
     masm.initGCThing(objReg, templateObject);
@@ -1950,17 +2238,17 @@ typedef JSObject *(*NewCallObjectFn)(JSC
 static const VMFunction NewCallObjectInfo =
     FunctionInfo<NewCallObjectFn>(NewCallObject);
 
 bool
 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
 {
     Register obj = ToRegister(lir->output());
 
-    JSObject *templateObj = lir->mir()->templateObj();
+    JSObject *templateObj = lir->mir()->templateObject();
 
     // If we have a template object, we can inline call object creation.
     OutOfLineCode *ool;
     if (lir->slots()->isRegister()) {
         ool = oolCallVM(NewCallObjectInfo, lir,
                         (ArgList(), ImmGCPtr(templateObj->lastProperty()),
                                     ImmGCPtr(templateObj->type()),
                                     ToRegister(lir->slots())),
@@ -1979,16 +2267,78 @@ CodeGenerator::visitNewCallObject(LNewCa
     masm.initGCThing(obj, templateObj);
 
     if (lir->slots()->isRegister())
         masm.storePtr(ToRegister(lir->slots()), Address(obj, JSObject::offsetOfSlots()));
     masm.bind(ool->rejoin());
     return true;
 }
 
+bool
+CodeGenerator::visitParNewCallObject(LParNewCallObject *lir)
+{
+    Register resultReg = ToRegister(lir->output());
+    Register parSliceReg = ToRegister(lir->parSlice());
+    Register tempReg1 = ToRegister(lir->getTemp0());
+    Register tempReg2 = ToRegister(lir->getTemp1());
+    JSObject *templateObj = lir->mir()->templateObj();
+
+    emitParAllocateGCThing(resultReg, parSliceReg, tempReg1, tempReg2, templateObj);
+
+    // NB: !lir->slots()->isRegister() implies that there is no slots
+    // array at all, and the memory is already zeroed when copying
+    // from the template object
+
+    if (lir->slots()->isRegister()) {
+        Register slotsReg = ToRegister(lir->slots());
+        JS_ASSERT(slotsReg != resultReg);
+        masm.storePtr(slotsReg, Address(resultReg, JSObject::offsetOfSlots()));
+    }
+
+    return true;
+}
+
+bool
+CodeGenerator::visitParNewDenseArray(LParNewDenseArray *lir)
+{
+    Register parSliceReg = ToRegister(lir->parSlice());
+    Register lengthReg = ToRegister(lir->length());
+    Register tempReg0 = ToRegister(lir->getTemp0());
+    Register tempReg1 = ToRegister(lir->getTemp1());
+    Register tempReg2 = ToRegister(lir->getTemp2());
+    JSObject *templateObj = lir->mir()->templateObject();
+
+    // Allocate the array into tempReg2.  Don't use resultReg because it
+    // may alias parSliceReg etc.
+    emitParAllocateGCThing(tempReg2, parSliceReg, tempReg0, tempReg1, templateObj);
+
+    // Invoke a C helper to allocate the elements.  For convenience,
+    // this helper also returns the array back to us, or NULL, which
+    // obviates the need to preserve the register across the call.  In
+    // reality, we should probably just have the C helper also
+    // *allocate* the array, but that would require that it initialize
+    // the various fields of the object, and I didn't want to
+    // duplicate the code in initGCThing() that already does such an
+    // admirable job.
+    masm.setupUnalignedABICall(3, CallTempReg3);
+    masm.passABIArg(parSliceReg);
+    masm.passABIArg(tempReg2);
+    masm.passABIArg(lengthReg);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParExtendArray));
+
+    Register resultReg = ToRegister(lir->output());
+    JS_ASSERT(resultReg == ReturnReg);
+    Label *bail;
+    if (!ensureOutOfLineParallelAbort(&bail))
+        return false;
+    masm.branchTestPtr(Assembler::Zero, resultReg, resultReg, bail);
+
+    return true;
+}
+
 typedef JSObject *(*NewStringObjectFn)(JSContext *, HandleString);
 static const VMFunction NewStringObjectInfo = FunctionInfo<NewStringObjectFn>(NewStringObject);
 
 bool
 CodeGenerator::visitNewStringObject(LNewStringObject *lir)
 {
     Register input = ToRegister(lir->input());
     Register output = ToRegister(lir->output());
@@ -2014,16 +2364,110 @@ CodeGenerator::visitNewStringObject(LNew
 }
 
 typedef bool(*InitPropFn)(JSContext *cx, HandleObject obj,
                           HandlePropertyName name, HandleValue value);
 static const VMFunction InitPropInfo =
     FunctionInfo<InitPropFn>(InitProp);
 
 bool
+CodeGenerator::visitParNew(LParNew *lir)
+{
+    Register objReg = ToRegister(lir->output());
+    Register parSliceReg = ToRegister(lir->parSlice());
+    Register tempReg1 = ToRegister(lir->getTemp0());
+    Register tempReg2 = ToRegister(lir->getTemp1());
+    JSObject *templateObject = lir->mir()->templateObject();
+    emitParAllocateGCThing(objReg, parSliceReg, tempReg1, tempReg2,
+                           templateObject);
+    return true;
+}
+
+class OutOfLineParNewGCThing : public OutOfLineCodeBase<CodeGenerator>
+{
+public:
+    gc::AllocKind allocKind;
+    Register objReg;
+
+    OutOfLineParNewGCThing(gc::AllocKind allocKind, Register objReg)
+        : allocKind(allocKind), objReg(objReg)
+    {}
+
+    bool accept(CodeGenerator *codegen) {
+        return codegen->visitOutOfLineParNewGCThing(this);
+    }
+};
+
+bool
+CodeGenerator::emitParAllocateGCThing(const Register &objReg,
+                                      const Register &parSliceReg,
+                                      const Register &tempReg1,
+                                      const Register &tempReg2,
+                                      JSObject *templateObj)
+{
+    gc::AllocKind allocKind = templateObj->getAllocKind();
+    OutOfLineParNewGCThing *ool = new OutOfLineParNewGCThing(allocKind, objReg);
+    if (!ool || !addOutOfLineCode(ool))
+        return false;
+
+    masm.parNewGCThing(objReg, parSliceReg, tempReg1, tempReg2,
+                       templateObj, ool->entry());
+    masm.bind(ool->rejoin());
+    masm.initGCThing(objReg, templateObj);
+    return true;
+}
+
+bool
+CodeGenerator::visitOutOfLineParNewGCThing(OutOfLineParNewGCThing *ool)
+{
+    // As a fallback for allocation in par. exec. mode, we invoke the
+    // C helper ParNewGCThing(), which calls into the GC code.  If it
+    // returns NULL, we bail.  If returns non-NULL, we rejoin the
+    // original instruction.
+
+    // This saves all caller-save registers, regardless of whether
+    // they are live.  This is wasteful but a simplification, given
+    // that for some of the LIR that this is used with
+    // (e.g., LParLambda) there are values in those registers
+    // that must not be clobbered but which are not technically
+    // considered live.
+    RegisterSet saveSet(RegisterSet::Volatile());
+
+    // Also preserve the temps we're about to overwrite,
+    // but don't bother to save the objReg.
+    saveSet.addUnchecked(CallTempReg0);
+    saveSet.addUnchecked(CallTempReg1);
+    saveSet.maybeTake(AnyRegister(ool->objReg));
+
+    masm.PushRegsInMask(saveSet);
+    masm.move32(Imm32(ool->allocKind), CallTempReg0);
+    masm.setupUnalignedABICall(1, CallTempReg1);
+    masm.passABIArg(CallTempReg0);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParNewGCThing));
+    masm.movePtr(ReturnReg, ool->objReg);
+    masm.PopRegsInMask(saveSet);
+    Label *bail;
+    if (!ensureOutOfLineParallelAbort(&bail))
+        return false;
+    masm.branchTestPtr(Assembler::Zero, ool->objReg, ool->objReg, bail);
+    masm.jump(ool->rejoin());
+    return true;
+}
+
+bool
+CodeGenerator::visitParBailout(LParBailout *lir)
+{
+    Label *bail;
+    if (!ensureOutOfLineParallelAbort(&bail))
+        return false;
+    masm.jump(bail);
+    return true;
+}
+
+bool
 CodeGenerator::visitInitProp(LInitProp *lir)
 {
     Register objReg = ToRegister(lir->getObject());
 
     pushArg(ToValue(lir, LInitProp::ValueIndex));
     pushArg(ImmGCPtr(lir->mir()->propertyName()));
     pushArg(objReg);
 
@@ -2366,16 +2810,44 @@ CodeGenerator::visitBinaryV(LBinaryV *li
         return callVM(UrshInfo, lir);
 
       default:
         JS_NOT_REACHED("Unexpected binary op");
         return false;
     }
 }
 
+bool
+CodeGenerator::visitParCompareS(LParCompareS *lir)
+{
+    JSOp op = lir->mir()->jsop();
+    Register left = ToRegister(lir->left());
+    Register right = ToRegister(lir->right());
+
+    JS_ASSERT((op == JSOP_EQ || op == JSOP_STRICTEQ) ||
+              (op == JSOP_NE || op == JSOP_STRICTNE));
+
+    masm.setupUnalignedABICall(2, CallTempReg2);
+    masm.passABIArg(left);
+    masm.passABIArg(right);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParCompareStrings));
+    masm.and32(Imm32(0xF), ReturnReg); // The C functions return an enum whose size is undef
+
+    // Check for cases that we do not currently handle in par exec
+    Label *bail;
+    if (!ensureOutOfLineParallelAbort(&bail))
+        return false;
+    masm.branch32(Assembler::Equal, ReturnReg, Imm32(ParCompareUnknown), bail);
+
+    if (op == JSOP_NE || op == JSOP_STRICTNE)
+        masm.xor32(Imm32(1), ReturnReg);
+
+    return true;
+}
+
 typedef bool (*StringCompareFn)(JSContext *, HandleString, HandleString, JSBool *);
 static const VMFunction stringsEqualInfo =
     FunctionInfo<StringCompareFn>(ion::StringsEqual<true>);
 static const VMFunction stringsNotEqualInfo =
     FunctionInfo<StringCompareFn>(ion::StringsEqual<false>);
 
 bool
 CodeGenerator::emitCompareS(LInstruction *lir, JSOp op, Register left, Register right,
@@ -2389,43 +2861,17 @@ CodeGenerator::emitCompareS(LInstruction
     } else {
         JS_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE);
         ool = oolCallVM(stringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
     }
 
     if (!ool)
         return false;
 
-    Label notPointerEqual;
-    // Fast path for identical strings
-    masm.branchPtr(Assembler::NotEqual, left, right, &notPointerEqual);
-    masm.move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), output);
-    masm.jump(ool->rejoin());
-
-    masm.bind(&notPointerEqual);
-    masm.loadPtr(Address(left, JSString::offsetOfLengthAndFlags()), output);
-    masm.loadPtr(Address(right, JSString::offsetOfLengthAndFlags()), temp);
-
-    Label notAtom;
-    // We can optimize the equality operation to a pointer compare for
-    // two atoms.
-    Imm32 atomBit(JSString::ATOM_BIT);
-    masm.branchTest32(Assembler::Zero, output, atomBit, &notAtom);
-    masm.branchTest32(Assembler::Zero, temp, atomBit, &notAtom);
-
-    masm.cmpPtr(left, right);
-    emitSet(JSOpToCondition(op), output);
-    masm.jump(ool->rejoin());
-
-    masm.bind(&notAtom);
-    // Strings of different length can never be equal.
-    masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), output);
-    masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp);
-    masm.branchPtr(Assembler::Equal, output, temp, ool->entry());
-    masm.move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), output);
+    masm.compareStrings(op, left, right, output, temp, ool->entry());
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitCompareStrictS(LCompareStrictS *lir)
 {
@@ -2579,17 +3025,17 @@ CodeGenerator::visitIsNullOrLikeUndefine
     JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
 
     Assembler::Condition cond = JSOpToCondition(op);
     if (compareType == MCompare::Compare_Null)
         cond = masm.testNull(cond, value);
     else
         cond = masm.testUndefined(cond, value);
 
-    emitSet(cond, output);
+    masm.emitSet(cond, output);
     return true;
 }
 
 bool
 CodeGenerator::visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir)
 {
     JSOp op = lir->mir()->jsop();
     MCompare::CompareType compareType = lir->mir()->compareType();
@@ -3125,27 +3571,35 @@ CodeGenerator::visitOutOfLineStoreElemen
         index = store->index();
         valueType = store->mir()->value()->type();
         if (store->value()->isConstant())
             value = ConstantOrRegister(*store->value()->toConstant());
         else
             value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
     }
 
+    // We can bump the initialized length inline if index ==
+    // initializedLength and index < capacity.  Otherwise, we have to
+    // consider fallback options.  In fallback cases, we branch to one
+    // of two labels because (at least in parallel mode) we can
+    // recover from index < capacity but not index !=
+    // initializedLength.
+    Label indexNotInitLen;
+    Label indexWouldExceedCapacity;
+
     // If index == initializedLength, try to bump the initialized length inline.
     // If index > initializedLength, call a stub. Note that this relies on the
     // condition flags sticking from the incoming branch.
-    Label callStub;
-    masm.j(Assembler::NotEqual, &callStub);
+    masm.j(Assembler::NotEqual, &indexNotInitLen);
 
     Int32Key key = ToInt32Key(index);
 
     // Check array capacity.
     masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
-                   key, &callStub);
+                   key, &indexWouldExceedCapacity);
 
     // Update initialized length. The capacity guard above ensures this won't overflow,
     // due to NELEMENTS_LIMIT.
     masm.bumpKey(&key, 1);
     masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength()));
 
     // Update length if length < initializedLength.
     Label dontUpdate;
@@ -3163,32 +3617,92 @@ CodeGenerator::visitOutOfLineStoreElemen
         storeElementTyped(ins->toStoreElementHoleT()->value(), valueType, MIRType_None, elements,
                           index);
         masm.jump(ool->rejoin());
     } else {
         // Jump to the inline path where we will store the value.
         masm.jump(ool->rejoinStore());
     }
 
-    masm.bind(&callStub);
-    saveLive(ins);
-
-    pushArg(Imm32(current->mir()->strict()));
-    pushArg(value);
-    if (index->isConstant())
-        pushArg(*index->toConstant());
-    else
-        pushArg(TypedOrValueRegister(MIRType_Int32, ToAnyRegister(index)));
-    pushArg(object);
-    if (!callVM(SetObjectElementInfo, ins))
-        return false;
-
-    restoreLive(ins);
-    masm.jump(ool->rejoin());
-    return true;
+    switch (gen->info().executionMode()) {
+      case SequentialExecution:
+        masm.bind(&indexNotInitLen);
+        masm.bind(&indexWouldExceedCapacity);
+        saveLive(ins);
+
+        pushArg(Imm32(current->mir()->strict()));
+        pushArg(value);
+        if (index->isConstant())
+            pushArg(*index->toConstant());
+        else
+            pushArg(TypedOrValueRegister(MIRType_Int32, ToAnyRegister(index)));
+        pushArg(object);
+        if (!callVM(SetObjectElementInfo, ins))
+            return false;
+
+        restoreLive(ins);
+        masm.jump(ool->rejoin());
+        return true;
+
+      case ParallelExecution:
+        Label *bail;
+        if (!ensureOutOfLineParallelAbort(&bail))
+            return false;
+
+        //////////////////////////////////////////////////////////////
+        // If the problem is that we do not have sufficient capacity,
+        // try to reallocate the elements array and then branch back
+        // to perform the actual write.  Note that we do not want to
+        // force the reg alloc to assign any particular register, so
+        // we make space on the stack and pass the arguments that way.
+        // (Also, outside of the VM call mechanism, it's very hard to
+        // pass in a Value to a C function!).
+        masm.bind(&indexWouldExceedCapacity);
+
+        // The use of registers here is somewhat subtle.  We need to
+        // save and restore the volatile registers but we also need to
+        // preserve the ReturnReg. Normally we'd just add a constraint
+        // to the regalloc, but since this is the slow path of a hot
+        // instruction we don't want to do that.  So instead we push
+        // the volatile registers but we don't save the register
+        // `object`.  We will copy the ReturnReg into `object`.  The
+        // function we are calling (`ParPush`) agrees to either return
+        // `object` unchanged or NULL.  This way after we restore the
+        // registers, we can examine `object` to know whether an error
+        // occurred.
+        RegisterSet saveSet(ins->safepoint()->liveRegs());
+        saveSet.maybeTake(object);
+
+        masm.PushRegsInMask(saveSet);
+        masm.reserveStack(sizeof(ParPushArgs));
+        masm.storePtr(object, Address(StackPointer, offsetof(ParPushArgs, object)));
+        masm.storeConstantOrRegister(value, Address(StackPointer,
+                                                    offsetof(ParPushArgs, value)));
+        masm.movePtr(StackPointer, CallTempReg0);
+        masm.setupUnalignedABICall(1, CallTempReg1);
+        masm.passABIArg(CallTempReg0);
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParPush));
+        masm.freeStack(sizeof(ParPushArgs));
+        masm.movePtr(ReturnReg, object);
+        masm.PopRegsInMask(saveSet);
+        masm.branchTestPtr(Assembler::Zero, object, object, bail);
+        masm.jump(ool->rejoin());
+
+        //////////////////////////////////////////////////////////////
+        // If the problem is that we are trying to write an index that
+        // is not the initialized length, that would result in a
+        // sparse array, and since we don't want to think about that
+        // case right now, we just bail out.
+        masm.bind(&indexNotInitLen);
+        masm.jump(bail);
+        return true;
+    }
+
+    JS_ASSERT(false);
+    return false;
 }
 
 typedef bool (*ArrayPopShiftFn)(JSContext *, HandleObject, MutableHandleValue);
 static const VMFunction ArrayPopDenseInfo = FunctionInfo<ArrayPopShiftFn>(ion::ArrayPopDense);
 static const VMFunction ArrayShiftDenseInfo = FunctionInfo<ArrayPopShiftFn>(ion::ArrayShiftDense);
 
 bool
 CodeGenerator::emitArrayPopShift(LInstruction *lir, const MArrayPopShift *mir, Register obj,
@@ -3551,17 +4065,17 @@ CodeGenerator::visitIteratorMore(LIterat
     LoadNativeIterator(masm, obj, output, ool->entry());
 
     masm.branchTest32(Assembler::NonZero, Address(output, offsetof(NativeIterator, flags)),
                       Imm32(JSITER_FOREACH), ool->entry());
 
     // Set output to true if props_cursor < props_end.
     masm.loadPtr(Address(output, offsetof(NativeIterator, props_end)), temp);
     masm.cmpPtr(Address(output, offsetof(NativeIterator, props_cursor)), temp);
-    emitSet(Assembler::LessThan, output);
+    masm.emitSet(Assembler::LessThan, output);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 typedef bool (*CloseIteratorFn)(JSContext *, HandleObject);
 static const VMFunction CloseIteratorInfo = FunctionInfo<CloseIteratorFn>(CloseIterator);
 
@@ -3694,17 +4208,18 @@ CodeGenerator::link()
     if (cx->compartment->types.compiledInfo.compilerOutput(cx)->isInvalidated())
         return true;
 
     IonScript *ionScript =
       IonScript::New(cx, graph.totalSlotCount(), scriptFrameSize, snapshots_.size(),
                      bailouts_.length(), graph.numConstants(),
                      safepointIndices_.length(), osiIndices_.length(),
                      cacheList_.length(), safepoints_.size(),
-                     graph.mir().numScripts());
+                     graph.mir().numScripts(),
+                     executionMode == ParallelExecution ? ForkJoinSlices(cx) : 0);
     SetIonScript(script, executionMode, ionScript);
 
     if (!ionScript)
         return false;
     invalidateEpilogueData_.fixup(&masm);
     Assembler::patchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_),
                                        ImmWord(uintptr_t(ionScript)),
                                        ImmWord(uintptr_t(-1)));
@@ -3733,16 +4248,19 @@ CodeGenerator::link()
     if (cacheList_.length())
         ionScript->copyCacheEntries(&cacheList_[0], masm);
     if (safepoints_.size())
         ionScript->copySafepoints(&safepoints_);
 
     JS_ASSERT(graph.mir().numScripts() > 0);
     ionScript->copyScriptEntries(graph.mir().scripts());
 
+    if (executionMode == ParallelExecution)
+        ionScript->zeroParallelInvalidatedScripts();
+
     linkAbsoluteLabels();
 
     // The correct state for prebarriers is unknown until the end of compilation,
     // since a GC can occur during code generation. All barriers are emitted
     // off-by-default, and are toggled on here if necessary.
     if (cx->zone()->needsBarrier())
         ionScript->toggleBarriers(true);
 
@@ -5085,11 +5603,24 @@ CodeGenerator::visitFunctionBoundary(LFu
             sps_.pop(masm, temp);
             return true;
 
         default:
             JS_NOT_REACHED("invalid LFunctionBoundary type");
     }
 }
 
+bool
+CodeGenerator::visitOutOfLineParallelAbort(OutOfLineParallelAbort *ool)
+{
+    masm.movePtr(ImmWord((void *) current->mir()->info().script()), CallTempReg0);
+    masm.setupUnalignedABICall(1, CallTempReg1);
+    masm.passABIArg(CallTempReg0);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParallelAbort));
+
+    masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
+    masm.jump(returnLabel_);
+    return true;
+}
+
 } // namespace ion
 } // namespace js
 
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -16,25 +16,29 @@
 # include "arm/CodeGenerator-arm.h"
 #else
 #error "CPU Not Supported"
 #endif
 
 namespace js {
 namespace ion {
 
+class OutOfLineNewParallelArray;
 class OutOfLineTestObject;
 class OutOfLineNewArray;
 class OutOfLineNewObject;
 class CheckOverRecursedFailure;
+class ParCheckOverRecursedFailure;
+class OutOfLineParCheckInterrupt;
 class OutOfLineUnboxDouble;
 class OutOfLineCache;
 class OutOfLineStoreElementHole;
 class OutOfLineTypeOfV;
 class OutOfLineLoadTypedArray;
+class OutOfLineParNewGCThing;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     bool generateArgumentsChecks();
     bool generateBody();
 
   public:
     CodeGenerator(MIRGenerator *gen, LIRGraph *graph);
@@ -67,16 +71,17 @@ class CodeGenerator : public CodeGenerat
     bool visitTestVAndBranch(LTestVAndBranch *lir);
     bool visitPolyInlineDispatch(LPolyInlineDispatch *lir);
     bool visitIntToString(LIntToString *lir);
     bool visitInteger(LInteger *lir);
     bool visitRegExp(LRegExp *lir);
     bool visitRegExpTest(LRegExpTest *lir);
     bool visitLambda(LLambda *lir);
     bool visitLambdaForSingleton(LLambdaForSingleton *lir);
+    bool visitParLambda(LParLambda *lir);
     bool visitPointer(LPointer *lir);
     bool visitSlots(LSlots *lir);
     bool visitStoreSlotV(LStoreSlotV *store);
     bool visitElements(LElements *lir);
     bool visitConvertElementsToDoubles(LConvertElementsToDoubles *lir);
     bool visitTypeBarrier(LTypeBarrier *lir);
     bool visitMonitorTypes(LMonitorTypes *lir);
     bool visitCallNative(LCallNative *call);
@@ -85,25 +90,30 @@ class CodeGenerator : public CodeGenerat
     bool visitCallGeneric(LCallGeneric *call);
     bool visitCallKnown(LCallKnown *call);
     bool emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize);
     void emitPushArguments(LApplyArgsGeneric *apply, Register extraStackSpace);
     void emitPopArguments(LApplyArgsGeneric *apply, Register extraStackSize);
     bool visitApplyArgsGeneric(LApplyArgsGeneric *apply);
     bool visitDoubleToInt32(LDoubleToInt32 *lir);
     bool visitNewSlots(LNewSlots *lir);
+    bool visitOutOfLineNewParallelArray(OutOfLineNewParallelArray *ool);
     bool visitNewArrayCallVM(LNewArray *lir);
     bool visitNewArray(LNewArray *lir);
     bool visitOutOfLineNewArray(OutOfLineNewArray *ool);
     bool visitNewObjectVMCall(LNewObject *lir);
     bool visitNewObject(LNewObject *lir);
     bool visitOutOfLineNewObject(OutOfLineNewObject *ool);
     bool visitNewDeclEnvObject(LNewDeclEnvObject *lir);
     bool visitNewCallObject(LNewCallObject *lir);
+    bool visitParNewCallObject(LParNewCallObject *lir);
     bool visitNewStringObject(LNewStringObject *lir);
+    bool visitParNew(LParNew *lir);
+    bool visitParNewDenseArray(LParNewDenseArray *lir);
+    bool visitParBailout(LParBailout *lir);
     bool visitInitProp(LInitProp *lir);
     bool visitCreateThis(LCreateThis *lir);
     bool visitCreateThisWithProto(LCreateThisWithProto *lir);
     bool visitCreateThisWithTemplate(LCreateThisWithTemplate *lir);
     bool visitReturnFromCtor(LReturnFromCtor *lir);
     bool visitArrayLength(LArrayLength *lir);
     bool visitTypedArrayLength(LTypedArrayLength *lir);
     bool visitTypedArrayElements(LTypedArrayElements *lir);
@@ -127,25 +137,29 @@ class CodeGenerator : public CodeGenerat
     bool visitMathFunctionD(LMathFunctionD *ins);
     bool visitModD(LModD *ins);
     bool visitMinMaxI(LMinMaxI *lir);
     bool visitBinaryV(LBinaryV *lir);
     bool emitCompareS(LInstruction *lir, JSOp op, Register left, Register right,
                       Register output, Register temp);
     bool visitCompareS(LCompareS *lir);
     bool visitCompareStrictS(LCompareStrictS *lir);
+    bool visitParCompareS(LParCompareS *lir);
     bool visitCompareVM(LCompareVM *lir);
     bool visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir);
     bool visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir);
     bool visitEmulatesUndefined(LEmulatesUndefined *lir);
     bool visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir);
     bool visitConcat(LConcat *lir);
     bool visitCharCodeAt(LCharCodeAt *lir);
     bool visitFromCharCode(LFromCharCode *lir);
     bool visitFunctionEnvironment(LFunctionEnvironment *lir);
+    bool visitParSlice(LParSlice *lir);
+    bool visitParWriteGuard(LParWriteGuard *lir);
+    bool visitParDump(LParDump *lir);
     bool visitCallGetProperty(LCallGetProperty *lir);
     bool visitCallGetElement(LCallGetElement *lir);
     bool visitCallSetElement(LCallSetElement *lir);
     bool visitThrow(LThrow *lir);
     bool visitTypeOfV(LTypeOfV *lir);
     bool visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool);
     bool visitToIdV(LToIdV *lir);
     bool visitLoadElementV(LLoadElementV *load);
@@ -191,27 +205,37 @@ class CodeGenerator : public CodeGenerat
     bool visitGetDOMProperty(LGetDOMProperty *lir);
     bool visitSetDOMProperty(LSetDOMProperty *lir);
     bool visitCallDOMNative(LCallDOMNative *lir);
     bool visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir);
 
     bool visitCheckOverRecursed(LCheckOverRecursed *lir);
     bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
 
+    bool visitParCheckOverRecursed(LParCheckOverRecursed *lir);
+    bool visitParCheckOverRecursedFailure(ParCheckOverRecursedFailure *ool);
+
+    bool visitParCheckInterrupt(LParCheckInterrupt *lir);
+    bool visitOutOfLineParCheckInterrupt(OutOfLineParCheckInterrupt *ool);
+
     bool visitUnboxDouble(LUnboxDouble *lir);
     bool visitOutOfLineUnboxDouble(OutOfLineUnboxDouble *ool);
     bool visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool);
 
     bool visitOutOfLineCacheGetProperty(OutOfLineCache *ool);
     bool visitOutOfLineGetElementCache(OutOfLineCache *ool);
     bool visitOutOfLineSetPropertyCache(OutOfLineCache *ool);
     bool visitOutOfLineBindNameCache(OutOfLineCache *ool);
     bool visitOutOfLineGetNameCache(OutOfLineCache *ool);
     bool visitOutOfLineCallsiteCloneCache(OutOfLineCache *ool);
 
+    bool visitOutOfLineParNewGCThing(OutOfLineParNewGCThing *ool);
+
+    bool visitOutOfLineParallelAbort(OutOfLineParallelAbort *ool);
+
     bool visitGetPropertyCacheV(LGetPropertyCacheV *ins) {
         return visitCache(ins);
     }
     bool visitGetPropertyCacheT(LGetPropertyCacheT *ins) {
         return visitCache(ins);
     }
     bool visitGetElementCacheV(LGetElementCacheV *ins) {
         return visitCache(ins);
@@ -231,19 +255,33 @@ class CodeGenerator : public CodeGenerat
     bool visitCallsiteCloneCache(LCallsiteCloneCache *ins) {
         return visitCache(ins);
     }
 
   private:
     bool visitCache(LInstruction *load);
     bool visitCallSetProperty(LInstruction *ins);
 
+    bool checkForParallelBailout();
+
     ConstantOrRegister getSetPropertyValue(LInstruction *ins);
     bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
 
+    bool emitParAllocateGCThing(const Register &objReg,
+                                const Register &threadContextReg,
+                                const Register &tempReg1,
+                                const Register &tempReg2,
+                                JSObject *templateObj);
+
+    bool emitParCallToUncompiledScript(Register calleeReg);
+
+    void emitLambdaInit(const Register &resultReg,
+                        const Register &scopeChainReg,
+                        JSFunction *fun);
+
     IonScriptCounts *maybeCreateScriptCounts();
 
     // Test whether value is truthy or not and jump to the corresponding label.
     // If the value can be an object that emulates |undefined|, |ool| must be
     // non-null; otherwise it may be null (and the scratch definitions should
     // be bogus), in which case an object encountered here will always be
     // truthy.
     void testValueTruthy(const ValueOperand &value,
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -12,16 +12,18 @@
 #include "IonSpewer.h"
 #include "LIR.h"
 #include "AliasAnalysis.h"
 #include "LICM.h"
 #include "ValueNumbering.h"
 #include "EdgeCaseAnalysis.h"
 #include "RangeAnalysis.h"
 #include "LinearScan.h"
+#include "vm/ParallelDo.h"
+#include "ParallelArrayAnalysis.h"
 #include "jscompartment.h"
 #include "vm/ThreadPool.h"
 #include "vm/ForkJoin.h"
 #include "IonCompartment.h"
 #include "CodeGenerator.h"
 #include "jsworkers.h"
 #include "BacktrackingAllocator.h"
 #include "StupidAllocator.h"
@@ -117,19 +119,16 @@ bool
 ion::InitializeIon()
 {
 #ifdef JS_THREADSAFE
     if (!IonTLSInitialized) {
         PRStatus status = PR_NewThreadPrivateIndex(&IonTLSIndex, NULL);
         if (status != PR_SUCCESS)
             return false;
 
-        if (!ForkJoinSlice::Initialize())
-            return false;
-
         IonTLSInitialized = true;
     }
 #endif
     CheckLogging();
     return true;
 }
 
 IonRuntime::IonRuntime()
@@ -465,29 +464,30 @@ IonScript::IonScript()
     osiIndexOffset_(0),
     osiIndexEntries_(0),
     cacheList_(0),
     cacheEntries_(0),
     safepointsStart_(0),
     safepointsSize_(0),
     scriptList_(0),
     scriptEntries_(0),
+    parallelInvalidatedScriptList_(0),
     refcount_(0),
     recompileInfo_(),
     slowCallCount(0)
 {
 }
 
 static const int DataAlignment = 4;
 
 IonScript *
 IonScript::New(JSContext *cx, uint32_t frameSlots, uint32_t frameSize, size_t snapshotsSize,
                size_t bailoutEntries, size_t constants, size_t safepointIndices,
                size_t osiIndices, size_t cacheEntries, size_t safepointsSize,
-               size_t scriptEntries)
+               size_t scriptEntries, size_t parallelInvalidatedScriptEntries)
 {
     if (snapshotsSize >= MAX_BUFFER_SIZE ||
         (bailoutEntries >= MAX_BUFFER_SIZE / sizeof(uint32_t)))
     {
         js_ReportOutOfMemory(cx);
         return NULL;
     }
 
@@ -497,24 +497,27 @@ IonScript::New(JSContext *cx, uint32_t f
     size_t paddedSnapshotsSize = AlignBytes(snapshotsSize, DataAlignment);
     size_t paddedBailoutSize = AlignBytes(bailoutEntries * sizeof(uint32_t), DataAlignment);
     size_t paddedConstantsSize = AlignBytes(constants * sizeof(Value), DataAlignment);
     size_t paddedSafepointIndicesSize = AlignBytes(safepointIndices * sizeof(SafepointIndex), DataAlignment);
     size_t paddedOsiIndicesSize = AlignBytes(osiIndices * sizeof(OsiIndex), DataAlignment);
     size_t paddedCacheEntriesSize = AlignBytes(cacheEntries * sizeof(IonCache), DataAlignment);
     size_t paddedSafepointSize = AlignBytes(safepointsSize, DataAlignment);
     size_t paddedScriptSize = AlignBytes(scriptEntries * sizeof(RawScript), DataAlignment);
+    size_t paddedParallelInvalidatedScriptSize =
+        AlignBytes(parallelInvalidatedScriptEntries * sizeof(RawScript), DataAlignment);
     size_t bytes = paddedSnapshotsSize +
                    paddedBailoutSize +
                    paddedConstantsSize +
                    paddedSafepointIndicesSize+
                    paddedOsiIndicesSize +
                    paddedCacheEntriesSize +
                    paddedSafepointSize +
-                   paddedScriptSize;
+                   paddedScriptSize +
+                   paddedParallelInvalidatedScriptSize;
     uint8_t *buffer = (uint8_t *)cx->malloc_(sizeof(IonScript) + bytes);
     if (!buffer)
         return NULL;
 
     IonScript *script = reinterpret_cast<IonScript *>(buffer);
     new (script) IonScript();
 
     uint32_t offsetCursor = sizeof(IonScript);
@@ -546,16 +549,20 @@ IonScript::New(JSContext *cx, uint32_t f
     script->safepointsStart_ = offsetCursor;
     script->safepointsSize_ = safepointsSize;
     offsetCursor += paddedSafepointSize;
 
     script->scriptList_ = offsetCursor;
     script->scriptEntries_ = scriptEntries;
     offsetCursor += paddedScriptSize;
 
+    script->parallelInvalidatedScriptList_ = offsetCursor;
+    script->parallelInvalidatedScriptEntries_ = parallelInvalidatedScriptEntries;
+    offsetCursor += parallelInvalidatedScriptEntries;
+
     script->frameSlots_ = frameSlots;
     script->frameSize_ = frameSize;
 
     script->recompileInfo_ = cx->compartment->types.compiledInfo;
 
     return script;
 }
 
@@ -602,16 +609,23 @@ IonScript::copyConstants(const HeapValue
 void
 IonScript::copyScriptEntries(JSScript **scripts)
 {
     for (size_t i = 0; i < scriptEntries_; i++)
         scriptList()[i] = scripts[i];
 }
 
 void
+IonScript::zeroParallelInvalidatedScripts()
+{
+    memset(parallelInvalidatedScriptList(), 0,
+           parallelInvalidatedScriptEntries_ * sizeof(JSScript *));
+}
+
+void
 IonScript::copySafepointIndices(const SafepointIndex *si, MacroAssembler &masm)
 {
     /*
      * Jumps in the caches reflect the offset of those jumps in the compiled
      * code, not the absolute positions of the jumps. Update according to the
      * final code address now.
      */
     SafepointIndex *table = safepointIndices();
@@ -767,192 +781,197 @@ ion::ToggleBarriers(JSCompartment *comp,
         if (script->hasIonScript())
             script->ion->toggleBarriers(needs);
     }
 }
 
 namespace js {
 namespace ion {
 
-CodeGenerator *
-CompileBackEnd(MIRGenerator *mir)
+bool
+OptimizeMIR(MIRGenerator *mir)
 {
     IonSpewPass("BuildSSA");
     // Note: don't call AssertGraphCoherency before SplitCriticalEdges,
     // the graph is not in RPO at this point.
 
     MIRGraph &graph = mir->graph();
 
     if (mir->shouldCancel("Start"))
-        return NULL;
+        return false;
 
     if (!SplitCriticalEdges(graph))
-        return NULL;
+        return false;
     IonSpewPass("Split Critical Edges");
     AssertGraphCoherency(graph);
 
     if (mir->shouldCancel("Split Critical Edges"))
-        return NULL;
+        return false;
 
     if (!RenumberBlocks(graph))
-        return NULL;
+        return false;
     IonSpewPass("Renumber Blocks");
     AssertGraphCoherency(graph);
 
     if (mir->shouldCancel("Renumber Blocks"))
-        return NULL;
+        return false;
 
     if (!BuildDominatorTree(graph))
-        return NULL;
+        return false;
     // No spew: graph not changed.
 
     if (mir->shouldCancel("Dominator Tree"))
-        return NULL;
+        return false;
 
     // This must occur before any code elimination.
     if (!EliminatePhis(mir, graph, AggressiveObservability))
-        return NULL;
+        return false;
     IonSpewPass("Eliminate phis");
     AssertGraphCoherency(graph);
 
     if (mir->shouldCancel("Eliminate phis"))
-        return NULL;
+        return false;
 
     if (!BuildPhiReverseMapping(graph))
-        return NULL;
+        return false;
     AssertExtendedGraphCoherency(graph);
     // No spew: graph not changed.
 
     if (mir->shouldCancel("Phi reverse mapping"))
-        return NULL;
+        return false;
 
     // This pass also removes copies.
     if (!ApplyTypeInformation(mir, graph))
-        return NULL;
+        return false;
     IonSpewPass("Apply types");
     AssertExtendedGraphCoherency(graph);
 
     if (mir->shouldCancel("Apply types"))
-        return NULL;
+        return false;
 
     // Alias analysis is required for LICM and GVN so that we don't move
     // loads across stores.
     if (js_IonOptions.licm || js_IonOptions.gvn) {
         AliasAnalysis analysis(mir, graph);
         if (!analysis.analyze())
-            return NULL;
+            return false;
         IonSpewPass("Alias analysis");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Alias analysis"))
-            return NULL;
+            return false;
 
         // Eliminating dead resume point operands requires basic block
         // instructions to be numbered. Reuse the numbering computed during
         // alias analysis.
         if (!EliminateDeadResumePointOperands(mir, graph))
-            return NULL;
+            return false;
 
         if (mir->shouldCancel("Eliminate dead resume point operands"))
-            return NULL;
+            return false;
     }
 
     if (js_IonOptions.gvn) {
         ValueNumberer gvn(mir, graph, js_IonOptions.gvnIsOptimistic);
         if (!gvn.analyze())
-            return NULL;
+            return false;
         IonSpewPass("GVN");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("GVN"))
-            return NULL;
+            return false;
     }
 
     if (js_IonOptions.uce) {
         UnreachableCodeElimination uce(mir, graph);
         if (!uce.analyze())
-            return NULL;
+            return false;
         IonSpewPass("UCE");
         AssertExtendedGraphCoherency(graph);
     }
 
     if (mir->shouldCancel("UCE"))
-        return NULL;
+        return false;
 
     if (js_IonOptions.licm) {
         LICM licm(mir, graph);
         if (!licm.analyze())
-            return NULL;
+            return false;
         IonSpewPass("LICM");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("LICM"))
-            return NULL;
+            return false;
     }
 
     if (js_IonOptions.rangeAnalysis) {
         RangeAnalysis r(graph);
         if (!r.addBetaNobes())
-            return NULL;
+            return false;
         IonSpewPass("Beta");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("RA Beta"))
-            return NULL;
+            return false;
 
         if (!r.analyze())
-            return NULL;
+            return false;
         IonSpewPass("Range Analysis");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("Range Analysis"))
-            return NULL;
+            return false;
 
         if (!r.removeBetaNobes())
-            return NULL;
+            return false;
         IonSpewPass("De-Beta");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("RA De-Beta"))
-            return NULL;
+            return false;
     }
 
     if (!EliminateDeadCode(mir, graph))
-        return NULL;
+        return false;
     IonSpewPass("DCE");
     AssertExtendedGraphCoherency(graph);
 
     if (mir->shouldCancel("DCE"))
-        return NULL;
+        return false;
 
     // Passes after this point must not move instructions; these analyses
     // depend on knowing the final order in which instructions will execute.
 
     if (js_IonOptions.edgeCaseAnalysis) {
         EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
         if (!edgeCaseAnalysis.analyzeLate())
-            return NULL;
+            return false;
         IonSpewPass("Edge Case Analysis (Late)");
         AssertGraphCoherency(graph);
 
         if (mir->shouldCancel("Edge Case Analysis (Late)"))
-            return NULL;
+            return false;
     }
 
     // Note: check elimination has to run after all other passes that move
     // instructions. Since check uses are replaced with the actual index, code
     // motion after this pass could incorrectly move a load or store before its
     // bounds check.
     if (!EliminateRedundantChecks(graph))
-        return NULL;
+        return false;
     IonSpewPass("Bounds Check Elimination");
     AssertGraphCoherency(graph);
 
-    if (mir->shouldCancel("Bounds Check Elimination"))
-        return NULL;
+    return true;
+}
+
+CodeGenerator *
+GenerateLIR(MIRGenerator *mir)
+{
+    MIRGraph &graph = mir->graph();
 
     LIRGraph *lir = mir->temp().lifoAlloc()->new_<LIRGraph>(&graph);
     if (!lir)
         return NULL;
 
     LIRGenerator lirgen(mir, graph, *lir);
     if (!lirgen.generate())
         return NULL;
@@ -1023,22 +1042,31 @@ CompileBackEnd(MIRGenerator *mir)
     if (!codegen || !codegen->generate()) {
         js_delete(codegen);
         return NULL;
     }
 
     return codegen;
 }
 
+CodeGenerator *
+CompileBackEnd(MIRGenerator *mir)
+{
+    if (!OptimizeMIR(mir))
+        return NULL;
+    return GenerateLIR(mir);
+}
+
 class SequentialCompileContext {
 public:
     ExecutionMode executionMode() {
         return SequentialExecution;
     }
 
+    MethodStatus checkScriptSize(JSContext *cx, UnrootedScript script);
     AbortReason compile(IonBuilder *builder, MIRGraph *graph,
                         ScopedJSDeletePtr<LifoAlloc> &autoDelete);
 };
 
 void
 AttachFinishedCompilations(JSContext *cx)
 {
 #ifdef JS_THREADSAFE
@@ -1222,39 +1250,16 @@ SequentialCompileContext::compile(IonBui
 
     bool success = codegen->link();
 
     IonSpewEndFunction();
 
     return success ? AbortReason_NoAbort : AbortReason_Disable;
 }
 
-MethodStatus
-TestIonCompile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing)
-{
-    SequentialCompileContext compileContext;
-
-    AbortReason reason = IonCompile(cx, script, fun, osrPc, constructing, compileContext);
-
-    if (reason == AbortReason_Alloc)
-        return Method_Skipped;
-
-    if (reason == AbortReason_Inlining)
-        return Method_Skipped;
-
-    if (reason == AbortReason_Disable) {
-        if (!cx->isExceptionPending())
-            ForbidCompilation(cx, script);
-        return Method_CantCompile;
-    }
-
-    JS_ASSERT(reason == AbortReason_NoAbort);
-    return Method_Compiled;
-}
-
 static bool
 CheckFrame(AbstractFramePtr fp)
 {
     if (fp.isEvalFrame()) {
         // Eval frames are not yet supported. Supporting this will require new
         // logic in pushBailoutFrame to deal with linking prev.
         // Additionally, JSOP_DEFVAR support will require baking in isEvalFrame().
         IonSpew(IonSpew_Abort, "eval frame");
@@ -1297,18 +1302,18 @@ CheckScript(UnrootedScript script)
     if (!script->compileAndGo) {
         IonSpew(IonSpew_Abort, "not compile-and-go");
         return false;
     }
 
     return true;
 }
 
-static MethodStatus
-CheckScriptSize(JSContext *cx, UnrootedScript script)
+MethodStatus
+SequentialCompileContext::checkScriptSize(JSContext *cx, UnrootedScript script)
 {
     if (!js_IonOptions.limitScriptSize)
         return Method_Compiled;
 
     // Longer scripts can only be compiled off thread, as these compilations
     // can be expensive and stall the main thread for too long.
     static const uint32_t MAX_MAIN_THREAD_SCRIPT_SIZE = 2000;
     static const uint32_t MAX_OFF_THREAD_SCRIPT_SIZE = 20000;
@@ -1340,62 +1345,67 @@ CheckScriptSize(JSContext *cx, UnrootedS
     if (numLocalsAndArgs > MAX_LOCALS_AND_ARGS) {
         IonSpew(IonSpew_Abort, "Too many locals and arguments (%u)", numLocalsAndArgs);
         return Method_CantCompile;
     }
 
     return Method_Compiled;
 }
 
+template <typename CompileContext>
 static MethodStatus
-Compile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
+Compile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing,
+        CompileContext &compileContext)
 {
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT_IF(osrPc != NULL, (JSOp)*osrPc == JSOP_LOOPENTRY);
 
     if (cx->compartment->debugMode()) {
         IonSpew(IonSpew_Abort, "debugging");
         return Method_CantCompile;
     }
 
     if (!CheckScript(script)) {
         IonSpew(IonSpew_Abort, "Aborted compilation of %s:%d", script->filename, script->lineno);
         return Method_CantCompile;
     }
 
-    MethodStatus status = CheckScriptSize(cx, script);
+    MethodStatus status = compileContext.checkScriptSize(cx, script);
     if (status != Method_Compiled) {
         IonSpew(IonSpew_Abort, "Aborted compilation of %s:%d", script->filename, script->lineno);
         return status;
     }
 
-    if (script->ion) {
-        if (!script->ion->method())
+    ExecutionMode executionMode = compileContext.executionMode();
+    IonScript *scriptIon = GetIonScript(script, executionMode);
+    if (scriptIon) {
+        if (!scriptIon->method())
             return Method_CantCompile;
         return Method_Compiled;
     }
 
-    if (cx->methodJitEnabled) {
-        // If JM is enabled we use getUseCount instead of incUseCount to avoid
-        // bumping the use count twice.
-        if (script->getUseCount() < js_IonOptions.usesBeforeCompile)
-            return Method_Skipped;
-    } else {
-        if (script->incUseCount() < js_IonOptions.usesBeforeCompileNoJaeger)
-            return Method_Skipped;
+    if (executionMode == SequentialExecution) {
+        if (cx->methodJitEnabled) {
+            // If JM is enabled we use getUseCount instead of incUseCount to avoid
+            // bumping the use count twice.
+
+            if (script->getUseCount() < js_IonOptions.usesBeforeCompile)
+                return Method_Skipped;
+        } else {
+            if (script->incUseCount() < js_IonOptions.usesBeforeCompileNoJaeger)
+                return Method_Skipped;
+        }
     }
 
-    SequentialCompileContext compileContext;
-
     AbortReason reason = IonCompile(cx, script, fun, osrPc, constructing, compileContext);
     if (reason == AbortReason_Disable)
         return Method_CantCompile;
 
     // Compilation succeeded or we invalidated right away or an inlining/alloc abort
-    return script->hasIonScript() ? Method_Compiled : Method_Skipped;
+    return HasIonScript(script, executionMode) ? Method_Compiled : Method_Skipped;
 }
 
 } // namespace ion
 } // namespace js
 
 // Decide if a transition from interpreter execution to Ion code should occur.
 // May compile or recompile the target JSScript.
 MethodStatus
@@ -1423,25 +1433,27 @@ ion::CanEnterAtBranch(JSContext *cx, JSS
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    JSFunction *fun = fp.isFunctionFrame() ? fp.fun() : NULL;
-    MethodStatus status = Compile(cx, script, fun, pc, isConstructing);
+    RootedFunction fun(cx, fp.isFunctionFrame() ? fp.fun() : NULL);
+    SequentialCompileContext compileContext;
+    RootedScript rscript(cx, script);
+    MethodStatus status = Compile(cx, rscript, fun, pc, isConstructing, compileContext);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
-    if (script->ion->osrPc() != pc)
+    if (script->ion && script->ion->osrPc() != pc)
         return Method_Skipped;
 
     return Method_Compiled;
 }
 
 MethodStatus
 ion::CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp,
               bool isConstructing, bool newType)
@@ -1475,28 +1487,159 @@ ion::CanEnter(JSContext *cx, JSScript *s
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    JSFunction *fun = fp.isFunctionFrame() ? fp.fun() : NULL;
-    MethodStatus status = Compile(cx, script, fun, NULL, isConstructing);
+    RootedFunction fun(cx, fp.isFunctionFrame() ? fp.fun() : NULL);
+    SequentialCompileContext compileContext;
+    RootedScript rscript(cx, script);
+    MethodStatus status = Compile(cx, rscript, fun, NULL, isConstructing, compileContext);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
 }
 
 MethodStatus
+ParallelCompileContext::checkScriptSize(JSContext *cx, UnrootedScript script)
+{
+    if (!js_IonOptions.limitScriptSize)
+        return Method_Compiled;
+
+    // When compiling for parallel execution we don't have off-thread
+    // compilation. We also up the max script size of the kernels.
+    static const uint32_t MAX_SCRIPT_SIZE = 5000;
+    static const uint32_t MAX_LOCALS_AND_ARGS = 256;
+
+    if (script->length > MAX_SCRIPT_SIZE) {
+        IonSpew(IonSpew_Abort, "Script too large (%u bytes)", script->length);
+        return Method_CantCompile;
+    }
+
+    uint32_t numLocalsAndArgs = analyze::TotalSlots(script);
+    if (numLocalsAndArgs > MAX_LOCALS_AND_ARGS) {
+        IonSpew(IonSpew_Abort, "Too many locals and arguments (%u)", numLocalsAndArgs);
+        return Method_CantCompile;
+    }
+
+    return Method_Compiled;
+}
+
+MethodStatus
+ParallelCompileContext::compileTransitively()
+{
+    using parallel::SpewBeginCompile;
+    using parallel::SpewEndCompile;
+
+    if (worklist_.empty())
+        return Method_Skipped;
+
+    RootedFunction fun(cx_);
+    RootedScript script(cx_);
+    while (!worklist_.empty()) {
+        fun = worklist_.back()->toFunction();
+        script = fun->nonLazyScript();
+        worklist_.popBack();
+
+        SpewBeginCompile(fun);
+
+        // If we had invalidations last time the parallel script run, add the
+        // invalidated scripts to the worklist.
+        if (script->hasParallelIonScript()) {
+            IonScript *ion = script->parallelIonScript();
+            JS_ASSERT(ion->parallelInvalidatedScriptEntries() > 0);
+
+            RootedFunction invalidFun(cx_);
+            for (uint32_t i = 0; i < ion->parallelInvalidatedScriptEntries(); i++) {
+                if (JSScript *invalid = ion->getAndZeroParallelInvalidatedScript(i)) {
+                    invalidFun = invalid->function();
+                    parallel::Spew(parallel::SpewCompile,
+                                   "Adding previously invalidated function %p:%s:%u",
+                                   fun.get(), invalid->filename, invalid->lineno);
+                    appendToWorklist(invalidFun);
+                }
+            }
+        }
+
+        // Attempt compilation. Returns Method_Compiled if already compiled.
+        MethodStatus status = Compile(cx_, script, fun, NULL, false, *this);
+        if (status != Method_Compiled) {
+            if (status == Method_CantCompile)
+                ForbidCompilation(cx_, script, ParallelExecution);
+            return SpewEndCompile(status);
+        }
+
+        // This can GC, so afterward, script->parallelIon is not guaranteed to be valid.
+        if (!cx_->compartment->ionCompartment()->enterJIT())
+            return SpewEndCompile(Method_Error);
+
+        // Subtle: it is possible for GC to occur during compilation of
+        // one of the invoked functions, which would cause the earlier
+        // functions (such as the kernel itself) to be collected.  In this
+        // event, we give up and fallback to sequential for now.
+        if (!script->hasParallelIonScript()) {
+            parallel::Spew(parallel::SpewCompile,
+                           "Function %p:%s:%u was garbage-collected or invalidated",
+                           fun.get(), script->filename, script->lineno);
+            return SpewEndCompile(Method_Skipped);
+        }
+
+        SpewEndCompile(Method_Compiled);
+    }
+
+    return Method_Compiled;
+}
+
+AbortReason
+ParallelCompileContext::compile(IonBuilder *builder,
+                                MIRGraph *graph,
+                                ScopedJSDeletePtr<LifoAlloc> &autoDelete)
+{
+    JS_ASSERT(!builder->script()->parallelIon);
+
+    RootedScript builderScript(cx_, builder->script());
+    IonSpewNewFunction(graph, builderScript);
+
+    if (!builder->build())
+        return builder->abortReason();
+    builder->clearForBackEnd();
+
+    // For the time being, we do not enable parallel compilation.
+
+    if (!OptimizeMIR(builder)) {
+        IonSpew(IonSpew_Abort, "Failed during back-end compilation.");
+        return AbortReason_Disable;
+    }
+
+    if (!analyzeAndGrowWorklist(builder, *graph)) {
+        return AbortReason_Disable;
+    }
+
+    CodeGenerator *codegen = GenerateLIR(builder);
+    if (!codegen)  {
+        IonSpew(IonSpew_Abort, "Failed during back-end compilation.");
+        return AbortReason_Disable;
+    }
+
+    bool success = codegen->link();
+    js_delete(codegen);
+
+    IonSpewEndFunction();
+
+    return success ? AbortReason_NoAbort : AbortReason_Disable;
+}
+
+MethodStatus
 ion::CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs)
 {
     JS_ASSERT(ion::IsEnabled(cx));
 
     // Skip if the code is expected to result in a bailout.
     if (!script->hasIonScript() || script->ion->bailoutExpected())
         return Method_Skipped;
 
@@ -1948,48 +2091,70 @@ ion::Invalidate(types::TypeCompartment &
 void
 ion::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses)
 {
     AutoAssertNoGC nogc;
     ion::Invalidate(cx->compartment->types, cx->runtime->defaultFreeOp(), invalid, resetUses);
 }
 
 bool
-ion::Invalidate(JSContext *cx, UnrootedScript script, bool resetUses)
+ion::Invalidate(JSContext *cx, UnrootedScript script, ExecutionMode mode, bool resetUses)
 {
     AutoAssertNoGC nogc;
     JS_ASSERT(script->hasIonScript());
 
     Vector<types::RecompileInfo> scripts(cx);
-    if (!scripts.append(script->ionScript()->recompileInfo()))
-        return false;
+
+    switch (mode) {
+      case SequentialExecution:
+        JS_ASSERT(script->hasIonScript());
+        if (!scripts.append(script->ionScript()->recompileInfo()))
+            return false;
+        break;
+      case ParallelExecution:
+        JS_ASSERT(script->hasParallelIonScript());
+        if (!scripts.append(script->parallelIonScript()->recompileInfo()))
+            return false;
+        break;
+    }
 
     Invalidate(cx, scripts, resetUses);
     return true;
 }
 
+bool
+ion::Invalidate(JSContext *cx, UnrootedScript script, bool resetUses)
+{
+    return Invalidate(cx, script, SequentialExecution, resetUses);
+}
+
+static void
+FinishInvalidationOf(FreeOp *fop, UnrootedScript script, IonScript **ionField)
+{
+    // If this script has Ion code on the stack, invalidation() will return
+    // true. In this case we have to wait until destroying it.
+    if (!(*ionField)->invalidated()) {
+        types::TypeCompartment &types = script->compartment()->types;
+        (*ionField)->recompileInfo().compilerOutput(types)->invalidate();
+
+        ion::IonScript::Destroy(fop, *ionField);
+    }
+
+    // In all cases, NULL out script->ion to avoid re-entry.
+    *ionField = NULL;
+}
+
 void
 ion::FinishInvalidation(FreeOp *fop, UnrootedScript script)
 {
-    if (!script->hasIonScript())
-        return;
+    if (script->hasIonScript())
+        FinishInvalidationOf(fop, script, &script->ion);
 
-    /*
-     * If this script has Ion code on the stack, invalidation() will return
-     * true. In this case we have to wait until destroying it.
-     */
-    if (!script->ion->invalidated()) {
-        types::TypeCompartment &types = script->compartment()->types;
-        script->ion->recompileInfo().compilerOutput(types)->invalidate();
-
-        ion::IonScript::Destroy(fop, script->ion);
-    }
-
-    /* In all cases, NULL out script->ion to avoid re-entry. */
-    script->ion = NULL;
+    if (script->hasParallelIonScript())
+        FinishInvalidationOf(fop, script, &script->parallelIon);
 }
 
 void
 ion::MarkValueFromIon(JSRuntime *rt, Value *vp)
 {
     gc::MarkValueUnbarriered(&rt->gcMarker, vp, "write barrier");
 }
 
@@ -1997,32 +2162,53 @@ void
 ion::MarkShapeFromIon(JSRuntime *rt, Shape **shapep)
 {
     gc::MarkShapeUnbarriered(&rt->gcMarker, shapep, "write barrier");
 }
 
 void
 ion::ForbidCompilation(JSContext *cx, UnrootedScript script)
 {
-    IonSpew(IonSpew_Abort, "Disabling Ion compilation of script %s:%d",
-            script->filename, script->lineno);
+    ForbidCompilation(cx, script, SequentialExecution);
+}
+
+void
+ion::ForbidCompilation(JSContext *cx, UnrootedScript script, ExecutionMode mode)
+{
+    IonSpew(IonSpew_Abort, "Disabling Ion mode %d compilation of script %s:%d",
+            mode, script->filename, script->lineno);
 
     CancelOffThreadIonCompile(cx->compartment, script);
 
-    if (script->hasIonScript()) {
-        // It is only safe to modify script->ion if the script is not currently
-        // running, because IonFrameIterator needs to tell what ionScript to
-        // use (either the one on the JSScript, or the one hidden in the
-        // breadcrumbs Invalidation() leaves). Therefore, if invalidation
-        // fails, we cannot disable the script.
-        if (!Invalidate(cx, script, false))
-            return;
+    switch (mode) {
+      case SequentialExecution:
+        if (script->hasIonScript()) {
+            // It is only safe to modify script->ion if the script is not currently
+            // running, because IonFrameIterator needs to tell what ionScript to
+            // use (either the one on the JSScript, or the one hidden in the
+            // breadcrumbs Invalidation() leaves). Therefore, if invalidation
+            // fails, we cannot disable the script.
+            if (!Invalidate(cx, script, mode, false))
+                return;
+        }
+
+        script->ion = ION_DISABLED_SCRIPT;
+        return;
+
+      case ParallelExecution:
+        if (script->hasParallelIonScript()) {
+            if (!Invalidate(cx, script, mode, false))
+                return;
+        }
+
+        script->parallelIon = ION_DISABLED_SCRIPT;
+        return;
     }
 
-    script->ion = ION_DISABLED_SCRIPT;
+    JS_NOT_REACHED("No such execution mode");
 }
 
 uint32_t
 ion::UsesBeforeIonRecompile(UnrootedScript script, jsbytecode *pc)
 {
     JS_ASSERT(pc == script->code || JSOp(*pc) == JSOP_LOOPENTRY);
 
     uint32_t minUses = js_IonOptions.usesBeforeCompile;
@@ -2096,17 +2282,17 @@ AutoFlushInhibitor::~AutoFlushInhibitor(
 int js::ion::LabelBase::id_count = 0;
 
 void
 ion::PurgeCaches(UnrootedScript script, JSCompartment *c) {
     if (script->hasIonScript())
         script->ion->purgeCaches(c);
 
     if (script->hasParallelIonScript())
-        script->ion->purgeCaches(c);
+        script->parallelIon->purgeCaches(c);
 }
 
 size_t
 ion::MemoryUsed(UnrootedScript script, JSMallocSizeOfFun mallocSizeOf) {
     size_t result = 0;
 
     if (script->hasIonScript())
         result += script->ion->sizeOfIncludingThis(mallocSizeOf);
--- a/js/src/ion/Ion.h
+++ b/js/src/ion/Ion.h
@@ -6,23 +6,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(jsion_ion_h__) && defined(JS_ION)
 #define jsion_ion_h__
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "IonCode.h"
+#include "CompileInfo.h"
 #include "jsinfer.h"
 #include "jsinterp.h"
 
 namespace js {
 namespace ion {
 
 class TempAllocator;
+class ParallelCompileContext; // in ParallelArrayAnalysis.h
 
 // Possible register allocators which may be used.
 enum IonRegisterAllocator {
     RegisterAllocator_LSRA,
     RegisterAllocator_Backtracking,
     RegisterAllocator_Stupid
 };
 
@@ -168,16 +170,21 @@ struct IonOptions
 
     // When caller runs in IM, but callee not, we take a slow path to the interpreter.
     // This has a significant overhead. In order to decrease the number of times this happens,
     // the useCount gets incremented faster to compile this function in IM and use the fastpath.
     //
     // Default: 5
     uint32_t slowCallIncUseCount;
 
+    // How many uses of a parallel kernel before we attempt compilation.
+    //
+    // Default: 1
+    uint32_t usesBeforeCompileParallel;
+
     void setEagerCompilation() {
         eagerCompilation = true;
         usesBeforeCompile = usesBeforeCompileNoJaeger = 0;
 
         // Eagerly inline calls to improve test coverage.
         usesBeforeInlining = 0;
         smallFunctionUsesBeforeInlining = 0;
 
@@ -204,17 +211,18 @@ struct IonOptions
         smallFunctionMaxInlineDepth(10),
         smallFunctionMaxBytecodeLength(100),
         smallFunctionUsesBeforeInlining(usesBeforeInlining / 4),
         polyInlineMax(4),
         inlineMaxTotalBytecodeLength(800),
         inlineUseCountRatio(128),
         eagerCompilation(false),
         slowCallLimit(512),
-        slowCallIncUseCount(5)
+        slowCallIncUseCount(5),
+        usesBeforeCompileParallel(1)
     {
     }
 };
 
 enum MethodStatus
 {
     Method_Error,
     Method_CantCompile,
@@ -296,38 +304,39 @@ IonExecStatus SideCannon(JSContext *cx, 
 
 // Used to enter Ion from C++ natives like Array.map. Called from FastInvokeGuard.
 IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgsList &args);
 
 // Walk the stack and invalidate active Ion frames for the invalid scripts.
 void Invalidate(types::TypeCompartment &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
 void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
+bool Invalidate(JSContext *cx, UnrootedScript script, ExecutionMode mode, bool resetUses = true);
 bool Invalidate(JSContext *cx, UnrootedScript script, bool resetUses = true);
 
 void MarkValueFromIon(JSRuntime *rt, Value *vp);
 void MarkShapeFromIon(JSRuntime *rt, Shape **shapep);
 
 void ToggleBarriers(JSCompartment *comp, bool needs);
 
 class IonBuilder;
 class MIRGenerator;
 class CodeGenerator;
 
 CodeGenerator *CompileBackEnd(MIRGenerator *mir);
 void AttachFinishedCompilations(JSContext *cx);
 void FinishOffThreadBuilder(IonBuilder *builder);
-MethodStatus TestIonCompile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing);
 
 static inline bool IsEnabled(JSContext *cx)
 {
-    return cx->hasRunOption(JSOPTION_ION) && cx->typeInferenceEnabled();
+    return cx->hasOption(JSOPTION_ION) && cx->typeInferenceEnabled();
 }
 
 void ForbidCompilation(JSContext *cx, UnrootedScript script);
+void ForbidCompilation(JSContext *cx, UnrootedScript script, ExecutionMode mode);
 uint32_t UsesBeforeIonRecompile(UnrootedScript script, jsbytecode *pc);
 
 void PurgeCaches(UnrootedScript script, JSCompartment *c);
 size_t MemoryUsed(UnrootedScript script, JSMallocSizeOfFun mallocSizeOf);
 void DestroyIonScripts(FreeOp *fop, UnrootedScript script);
 void TraceIonScripts(JSTracer* trc, UnrootedScript script);
 
 } // namespace ion
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -4502,16 +4502,26 @@ IonBuilder::jsop_initprop(HandleProperty
     TypeOracle::BinaryTypes b = oracle->binaryTypes(script(), pc);
     if (b.lhsTypes &&
         (id == types::IdToTypeId(id)) &&
         !b.lhsTypes->propertyNeedsBarrier(cx, id))
     {
         needsBarrier = false;
     }
 
+    // In parallel execution, we never require write barriers.  See
+    // forkjoin.cpp for more information.
+    switch (info().executionMode()) {
+      case SequentialExecution:
+        break;
+      case ParallelExecution:
+        needsBarrier = false;
+        break;
+    }
+
     if (templateObject->isFixedSlot(shape->slot())) {
         MStoreFixedSlot *store = MStoreFixedSlot::New(obj, shape->slot(), value);
         if (needsBarrier)
             store->setNeedsBarrier();
 
         current->add(store);
         return resumeAfter(store);
     }
@@ -5465,30 +5475,30 @@ IonBuilder::jsop_getelem_dense()
 
     if (knownType != JSVAL_TYPE_UNKNOWN)
         load->setResultType(MIRTypeFromValueType(knownType));
 
     current->push(load);
     return pushTypeBarrier(load, types, barrier);
 }
 
-static MInstruction *
-GetTypedArrayLength(MDefinition *obj)
+MInstruction *
+IonBuilder::getTypedArrayLength(MDefinition *obj)
 {
     if (obj->isConstant()) {
         JSObject *array = &obj->toConstant()->value().toObject();
         int32_t length = (int32_t) TypedArray::length(array);
         obj->setFoldedUnchecked();
         return MConstant::New(Int32Value(length));
     }
     return MTypedArrayLength::New(obj);
 }
 
-static MInstruction *
-GetTypedArrayElements(MDefinition *obj)
+MInstruction *
+IonBuilder::getTypedArrayElements(MDefinition *obj)
 {
     if (obj->isConstant()) {
         JSObject *array = &obj->toConstant()->value().toObject();
         void *data = TypedArray::viewData(array);
         obj->setFoldedUnchecked();
         return MConstantElements::New(data);
     }
     return MTypedArrayElements::New(obj);
@@ -5541,24 +5551,24 @@ IonBuilder::jsop_getelem_typed(int array
             knownType = MIRType_Double;
             break;
           default:
             JS_NOT_REACHED("Unknown typed array type");
             return false;
         }
 
         // Get the length.
-        MInstruction *length = GetTypedArrayLength(obj);
+        MInstruction *length = getTypedArrayLength(obj);
         current->add(length);
 
         // Bounds check.
         id = addBoundsCheck(id, length);
 
         // Get the elements vector.
-        MInstruction *elements = GetTypedArrayElements(obj);
+        MInstruction *elements = getTypedArrayElements(obj);
         current->add(elements);
 
         // Load the element.
         MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(elements, id, arrayType);
         current->add(load);
         current->push(load);
 
         load->setResultType(knownType);
@@ -5718,24 +5728,24 @@ IonBuilder::jsop_setelem_typed(int array
     MDefinition *obj = current->pop();
 
     // Ensure id is an integer.
     MInstruction *idInt32 = MToInt32::New(id);
     current->add(idInt32);
     id = idInt32;
 
     // Get the length.
-    MInstruction *length = GetTypedArrayLength(obj);
+    MInstruction *length = getTypedArrayLength(obj);
     current->add(length);
 
     // Bounds check.
     id = addBoundsCheck(id, length);
 
     // Get the elements vector.
-    MInstruction *elements = GetTypedArrayElements(obj);
+    MInstruction *elements = getTypedArrayElements(obj);
     current->add(elements);
 
     // Clamp value to [0, 255] for Uint8ClampedArray.
     MDefinition *unclampedValue = value;
     if (arrayType == TypedArray::TYPE_UINT8_CLAMPED) {
         value = MClampToUint8::New(value);
         current->add(value->toInstruction());
     }
@@ -5789,17 +5799,17 @@ IonBuilder::jsop_length_fastPath()
             MArrayLength *length = new MArrayLength(elements);
             current->add(length);
             current->push(length);
             return true;
         }
 
         if (sig.inTypes->getTypedArrayType() != TypedArray::TYPE_MAX) {
             MDefinition *obj = current->pop();
-            MInstruction *length = GetTypedArrayLength(obj);
+            MInstruction *length = getTypedArrayLength(obj);
             current->add(length);
             current->push(length);
             return true;
         }
 
         return false;
       }
 
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -305,16 +305,20 @@ class IonBuilder : public MIRGenerator
     bool getPropTryCommonGetter(bool *emitted, HandleId id, types::StackTypeSet *barrier,
                                 types::StackTypeSet *types, TypeOracle::UnaryTypes unaryTypes);
     bool getPropTryMonomorphic(bool *emitted, HandleId id, types::StackTypeSet *barrier,
                                TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes);
     bool getPropTryPolymorphic(bool *emitted, HandlePropertyName name, HandleId id,
                                types::StackTypeSet *barrier, types::StackTypeSet *types,
                                TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes);
 
+    // Typed array helpers.
+    MInstruction *getTypedArrayLength(MDefinition *obj);
+    MInstruction *getTypedArrayElements(MDefinition *obj);
+
     bool jsop_add(MDefinition *left, MDefinition *right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
     bool jsop_binary(JSOp op);
     bool jsop_binary(JSOp op, MDefinition *left, MDefinition *right);
     bool jsop_pos();
     bool jsop_neg();
     bool jsop_defvar(uint32_t index);
@@ -410,16 +414,28 @@ class IonBuilder : public MIRGenerator
     InliningStatus inlineStringObject(CallInfo &callInfo);
     InliningStatus inlineStrCharCodeAt(CallInfo &callInfo);
     InliningStatus inlineStrFromCharCode(CallInfo &callInfo);
     InliningStatus inlineStrCharAt(CallInfo &callInfo);
 
     // RegExp natives.
     InliningStatus inlineRegExpTest(CallInfo &callInfo);
 
+    // Parallel Array.
+    InliningStatus inlineUnsafeSetElement(CallInfo &callInfo);
+    bool inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base);
+    bool inlineUnsafeSetTypedArrayElement(CallInfo &callInfo, uint32_t base, int arrayType);
+    InliningStatus inlineForceSequentialOrInParallelSection(CallInfo &callInfo);
+    InliningStatus inlineNewDenseArray(CallInfo &callInfo);
+    InliningStatus inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo);
+    InliningStatus inlineNewDenseArrayForParallelExecution(CallInfo &callInfo);
+
+    InliningStatus inlineThrowError(CallInfo &callInfo);
+    InliningStatus inlineDump(CallInfo &callInfo);
+
     InliningStatus inlineNativeCall(CallInfo &callInfo, JSNative native);
 
     // Call functions
     bool jsop_call_inline(HandleFunction callee, CallInfo &callInfo, MBasicBlock *bottom,
                           Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns);
     bool inlineScriptedCalls(AutoObjectVector &targets, AutoObjectVector &originals,
                              CallInfo &callInfo);
     bool inlineScriptedCall(HandleFunction target, CallInfo &callInfo);
--- a/js/src/ion/IonCode.h
+++ b/js/src/ion/IonCode.h
@@ -206,16 +206,28 @@ struct IonScript
     // Offset to and length of the safepoint table in bytes.
     uint32_t safepointsStart_;
     uint32_t safepointsSize_;
 
     // List of compiled/inlined JSScript's.
     uint32_t scriptList_;
     uint32_t scriptEntries_;
 
+    // In parallel mode, list of scripts that we call that were invalidated
+    // last time this script bailed out. These will be recompiled (or tried to
+    // be) upon next parallel entry of this script.
+    //
+    // For non-parallel IonScripts, this is NULL.
+    //
+    // For parallel IonScripts, there are as many entries as there are slices,
+    // since for any single parallel execution, we can only get a single
+    // invalidation per slice.
+    uint32_t parallelInvalidatedScriptList_;
+    uint32_t parallelInvalidatedScriptEntries_;
+
     // Number of references from invalidation records.
     size_t refcount_;
 
     types::RecompileInfo recompileInfo_;
 
   public:
     // Number of times this function has tried to call a non-IM compileable function
     uint32_t slowCallCount;
@@ -239,28 +251,33 @@ struct IonScript
         return (OsiIndex *)(reinterpret_cast<uint8_t *>(this) + osiIndexOffset_);
     }
     IonCache *cacheList() {
         return (IonCache *)(reinterpret_cast<uint8_t *>(this) + cacheList_);
     }
     JSScript **scriptList() const {
         return (JSScript **)(reinterpret_cast<const uint8_t *>(this) + scriptList_);
     }
+    JSScript **parallelInvalidatedScriptList() {
+        return (JSScript **)(reinterpret_cast<const uint8_t *>(this) +
+                             parallelInvalidatedScriptList_);
+    }
 
   private:
     void trace(JSTracer *trc);
 
   public:
     // Do not call directly, use IonScript::New. This is public for cx->new_.
     IonScript();
 
     static IonScript *New(JSContext *cx, uint32_t frameLocals, uint32_t frameSize,
                           size_t snapshotsSize, size_t snapshotEntries,
                           size_t constants, size_t safepointIndexEntries, size_t osiIndexEntries,
-                          size_t cacheEntries, size_t safepointsSize, size_t scriptEntries);
+                          size_t cacheEntries, size_t safepointsSize, size_t scriptEntries,
+                          size_t parallelInvalidatedScriptEntries);
     static void Trace(JSTracer *trc, IonScript *script);
     static void Destroy(FreeOp *fop, IonScript *script);
 
     static inline size_t offsetOfMethod() {
         return offsetof(IonScript, method_);
     }
     static inline size_t offsetOfOsrEntryOffset() {
         return offsetof(IonScript, osrEntryOffset_);
@@ -334,16 +351,25 @@ struct IonScript
     }
     UnrootedScript getScript(size_t i) const {
         JS_ASSERT(i < scriptEntries_);
         return scriptList()[i];
     }
     size_t scriptEntries() const {
         return scriptEntries_;
     }
+    size_t parallelInvalidatedScriptEntries() const {
+        return parallelInvalidatedScriptEntries_;
+    }
+    RawScript getAndZeroParallelInvalidatedScript(uint32_t i) {
+        JS_ASSERT(i < parallelInvalidatedScriptEntries_);
+        RawScript script = parallelInvalidatedScriptList()[i];
+        parallelInvalidatedScriptList()[i] = NULL;
+        return script;
+    }
     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
         return mallocSizeOf(this);
     }
     HeapValue &getConstant(size_t index) {
         JS_ASSERT(index < numConstants());
         return constants()[index];
     }
     size_t numConstants() const {
@@ -375,16 +401,17 @@ struct IonScript
     void copySnapshots(const SnapshotWriter *writer);
     void copyBailoutTable(const SnapshotOffset *table);
     void copyConstants(const HeapValue *vp);
     void copySafepointIndices(const SafepointIndex *firstSafepointIndex, MacroAssembler &masm);
     void copyOsiIndices(const OsiIndex *firstOsiIndex, MacroAssembler &masm);
     void copyCacheEntries(const IonCache *caches, MacroAssembler &masm);
     void copySafepoints(const SafepointWriter *writer);
     void copyScriptEntries(JSScript **scripts);
+    void zeroParallelInvalidatedScripts();
 
     bool invalidated() const {
         return refcount_ != 0;
     }
     size_t refcount() const {
         return refcount_;
     }
     void incref() {
--- a/js/src/ion/IonMacroAssembler.cpp
+++ b/js/src/ion/IonMacroAssembler.cpp
@@ -328,16 +328,70 @@ MacroAssembler::newGCThing(const Registe
     branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(&list->last), result, fail);
 
     addPtr(Imm32(thingSize), result);
     storePtr(result, AbsoluteAddress(&list->first));
     subPtr(Imm32(thingSize), result);
 }
 
 void
+MacroAssembler::parNewGCThing(const Register &result,
+                              const Register &threadContextReg,
+                              const Register &tempReg1,
+                              const Register &tempReg2,
+                              JSObject *templateObject,
+                              Label *fail)
+{
+    // Similar to ::newGCThing(), except that it allocates from a
+    // custom Allocator in the ForkJoinSlice*, rather than being
+    // hardcoded to the compartment allocator.  This requires two
+    // temporary registers.
+    //
+    // Subtle: I wanted to reuse `result` for one of the temporaries,
+    // but the register allocator was assigning it to the same
+    // register as `threadContextReg`.  Then we overwrite that
+    // register which messed up the OOL code.
+
+    gc::AllocKind allocKind = templateObject->getAllocKind();
+    uint32_t thingSize = (uint32_t)gc::Arena::thingSize(allocKind);
+
+    // Load the allocator:
+    // tempReg1 = (Allocator*) forkJoinSlice->allocator
+    loadPtr(Address(threadContextReg, offsetof(js::ForkJoinSlice, allocator)),
+            tempReg1);
+
+    // Get a pointer to the relevant free list:
+    // tempReg1 = (FreeSpan*) &tempReg1->arenas.freeLists[(allocKind)]
+    uint32_t offset = (offsetof(Allocator, arenas) +
+                       js::gc::ArenaLists::getFreeListOffset(allocKind));
+    addPtr(Imm32(offset), tempReg1);
+
+    // Load first item on the list
+    // tempReg2 = tempReg1->first
+    loadPtr(Address(tempReg1, offsetof(gc::FreeSpan, first)), tempReg2);
+
+    // Check whether list is empty
+    // if tempReg1->last <= tempReg2, fail
+    branchPtr(Assembler::BelowOrEqual,
+              Address(tempReg1, offsetof(gc::FreeSpan, last)),
+              tempReg2,
+              fail);
+
+    // If not, take first and advance pointer by thingSize bytes.
+    // result = tempReg2;
+    // tempReg2 += thingSize;
+    movePtr(tempReg2, result);
+    addPtr(Imm32(thingSize), tempReg2);
+
+    // Update `first`
+    // tempReg1->first = tempReg2;
+    storePtr(tempReg2, Address(tempReg1, offsetof(gc::FreeSpan, first)));
+}
+
+void
 MacroAssembler::initGCThing(const Register &obj, JSObject *templateObject)
 {
     // Fast initialization of an empty object returned by NewGCThing().
 
     storePtr(ImmGCPtr(templateObject->lastProperty()), Address(obj, JSObject::offsetOfShape()));
     storePtr(ImmGCPtr(templateObject->type()), Address(obj, JSObject::offsetOfType()));
     storePtr(ImmWord((void *)NULL), Address(obj, JSObject::offsetOfSlots()));
 
@@ -374,16 +428,65 @@ MacroAssembler::initGCThing(const Regist
     if (templateObject->hasPrivate()) {
         uint32_t nfixed = templateObject->numFixedSlots();
         storePtr(ImmWord(templateObject->getPrivate()),
                  Address(obj, JSObject::getPrivateDataOffset(nfixed)));
     }
 }
 
 void
+MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result,
+                               Register temp, Label *fail)
+{
+    JS_ASSERT(IsEqualityOp(op));
+
+    Label done;
+    Label notPointerEqual;
+    // Fast path for identical strings.
+    branchPtr(Assembler::NotEqual, left, right, &notPointerEqual);
+    move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), result);
+    jump(&done);
+
+    bind(&notPointerEqual);
+    loadPtr(Address(left, JSString::offsetOfLengthAndFlags()), result);
+    loadPtr(Address(right, JSString::offsetOfLengthAndFlags()), temp);
+
+    Label notAtom;
+    // Optimize the equality operation to a pointer compare for two atoms.
+    Imm32 atomBit(JSString::ATOM_BIT);
+    branchTest32(Assembler::Zero, result, atomBit, &notAtom);
+    branchTest32(Assembler::Zero, temp, atomBit, &notAtom);
+
+    cmpPtr(left, right);
+    emitSet(JSOpToCondition(op), result);
+    jump(&done);
+
+    bind(&notAtom);
+    // Strings of different length can never be equal.
+    rshiftPtr(Imm32(JSString::LENGTH_SHIFT), result);
+    rshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp);
+    branchPtr(Assembler::Equal, result, temp, fail);
+    move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), result);
+
+    bind(&done);
+}
+
+void
+MacroAssembler::parCheckInterruptFlags(const Register &tempReg,
+                                       Label *fail)
+{
+    JSCompartment *compartment = GetIonContext()->compartment;
+
+    void *interrupt = (void*)&compartment->rt->interrupt;
+    movePtr(ImmWord(interrupt), tempReg);
+    load32(Address(tempReg, 0), tempReg);
+    branchTest32(Assembler::NonZero, tempReg, tempReg, fail);
+}
+
+void
 MacroAssembler::maybeRemoveOsrFrame(Register scratch)
 {
     // Before we link an exit frame, check for an OSR frame, which is
     // indicative of working inside an existing bailout. In this case, remove
     // the OSR frame, so we don't explode the stack with repeated bailouts.
     Label osrRemoved;
     loadPtr(Address(StackPointer, IonCommonFrameLayout::offsetOfDescriptor()), scratch);
     and32(Imm32(FRAMETYPE_MASK), scratch);
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -13,16 +13,19 @@
 #elif defined(JS_CPU_X64)
 # include "ion/x64/MacroAssembler-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "ion/arm/MacroAssembler-arm.h"
 #endif
 #include "ion/IonCompartment.h"
 #include "ion/IonInstrumentation.h"
 #include "ion/TypeOracle.h"
+#include "ion/ParallelFunctions.h"
+
+#include "vm/ForkJoin.h"
 
 #include "jstypedarray.h"
 #include "jscompartment.h"
 
 #include "vm/Shape.h"
 
 namespace js {
 namespace ion {
@@ -484,18 +487,34 @@ class MacroAssembler : public MacroAssem
     }
 
     // Inline version of js_TypedArray_uint8_clamp_double.
     // This function clobbers the input register.
     void clampDoubleToUint8(FloatRegister input, Register output);
 
     // Inline allocation.
     void newGCThing(const Register &result, JSObject *templateObject, Label *fail);
+    void parNewGCThing(const Register &result,
+                       const Register &threadContextReg,
+                       const Register &tempReg1,
+                       const Register &tempReg2,
+                       JSObject *templateObject,
+                       Label *fail);
     void initGCThing(const Register &obj, JSObject *templateObject);
 
+    // Compares two strings for equality based on the JSOP.
+    // This checks for identical pointers, atoms and length and fails for everything else.
+    void compareStrings(JSOp op, Register left, Register right, Register result,
+                        Register temp, Label *fail);
+
+    // Checks the flags that signal that parallel code may need to interrupt or
+    // abort.  Branches to fail in that case.
+    void parCheckInterruptFlags(const Register &tempReg,
+                                Label *fail);
+
     // If the IonCode that created this assembler needs to transition into the VM,
     // we want to store the IonCode on the stack in order to mark it during a GC.
     // This is a reference to a patch location where the IonCode* will be written.
   private:
     CodeOffsetLabel exitCodePatch_;
 
   public:
     void enterExitFrame(const VMFunction *f = NULL) {
--- a/js/src/ion/IonSpewer.cpp
+++ b/js/src/ion/IonSpewer.cpp
@@ -231,16 +231,17 @@ ion::CheckLogging()
             "  codegen    Native code generation\n"
             "  bailouts   Bailouts\n"
             "  caches     Inline caches\n"
             "  osi        Invalidation\n"
             "  safepoints Safepoints\n"
             "  pools      Literal Pools (ARM only for now)\n"
             "  cacheflush Instruction Cache flushes (ARM only for now)\n"
             "  logs       C1 and JSON visualization logging\n"
+            "  trace      Generate calls to js::ion::Trace() for effectful instructions\n"
             "  all        Everything\n"
             "\n"
         );
         exit(0);
         /*NOTREACHED*/
     }
     if (ContainsFlag(env, "aborts"))
         EnableChannel(IonSpew_Abort);
@@ -273,16 +274,18 @@ ion::CheckLogging()
     if (ContainsFlag(env, "safepoints"))
         EnableChannel(IonSpew_Safepoints);
     if (ContainsFlag(env, "pools"))
         EnableChannel(IonSpew_Pools);
     if (ContainsFlag(env, "cacheflush"))
         EnableChannel(IonSpew_CacheFlush);
     if (ContainsFlag(env, "logs"))
         EnableIonDebugLogging();
+    if (ContainsFlag(env, "trace"))
+        EnableChannel(IonSpew_Trace);
     if (ContainsFlag(env, "all"))
         LoggingBits = uint32_t(-1);
 
     if (LoggingBits != 0)
         EnableIonDebugLogging();
 
     IonSpewFile = stderr;
 }
--- a/js/src/ion/IonSpewer.h
+++ b/js/src/ion/IonSpewer.h
@@ -47,16 +47,18 @@ namespace ion {
     /* Debug info about snapshots */        \
     _(Snapshots)                            \
     /* Generated inline cache stubs */      \
     _(InlineCaches)                         \
     /* Debug info about safepoints */       \
     _(Safepoints)                           \
     /* Debug info about Pools*/             \
     _(Pools)                                \
+    /* Calls to js::ion::Trace() */         \
+    _(Trace)                                \
     /* Debug info about the I$ */           \
     _(CacheFlush)
 
 
 enum IonSpewChannel {
 #define IONSPEW_CHANNEL(name) IonSpew_##name,
     IONSPEW_CHANNEL_LIST(IONSPEW_CHANNEL)
 #undef IONSPEW_CHANNEL
--- a/js/src/ion/IonTypes.h
+++ b/js/src/ion/IonTypes.h
@@ -59,21 +59,22 @@ enum MIRType
     MIRType_Null,
     MIRType_Boolean,
     MIRType_Int32,
     MIRType_Double,
     MIRType_String,
     MIRType_Object,
     MIRType_Magic,
     MIRType_Value,
-    MIRType_None,       // Invalid, used as a placeholder.
-    MIRType_Slots,      // A slots vector
-    MIRType_Elements,   // An elements vector
-    MIRType_StackFrame, // StackFrame pointer for OSR.
-    MIRType_Shape       // A Shape pointer.
+    MIRType_None,         // Invalid, used as a placeholder.
+    MIRType_Slots,        // A slots vector
+    MIRType_Elements,     // An elements vector
+    MIRType_StackFrame,   // StackFrame pointer for OSR.
+    MIRType_Shape,        // A Shape pointer.
+    MIRType_ForkJoinSlice // js::ForkJoinSlice*
 };
 
 #ifdef DEBUG
 // Track the pipeline of opcodes which has produced a snapshot.
 #define TRACK_SNAPSHOTS 1
 #endif
 
 } // namespace ion
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -244,16 +244,26 @@ class LNewSlots : public LCallInstructio
         return getTemp(2);
     }
 
     MNewSlots *mir() const {
         return mir_->toNewSlots();
     }
 };
 
+class LNewParallelArray : public LInstructionHelper<1, 0, 0>
+{
+  public:
+    LIR_HEADER(NewParallelArray);
+
+    MNewParallelArray *mir() const {
+        return mir_->toNewParallelArray();
+    }
+};
+
 class LNewArray : public LInstructionHelper<1, 0, 0>
 {
   public:
     LIR_HEADER(NewArray)
 
     MNewArray *mir() const {
         return mir_->toNewArray();
     }
@@ -264,16 +274,89 @@ class LNewObject : public LInstructionHe
   public:
     LIR_HEADER(NewObject)
 
     MNewObject *mir() const {
         return mir_->toNewObject();
     }
 };
 
+class LParNew : public LInstructionHelper<1, 1, 2>
+{
+  public:
+    LIR_HEADER(ParNew);
+
+    LParNew(const LAllocation &parSlice,
+            const LDefinition &temp1,
+            const LDefinition &temp2)
+    {
+        setOperand(0, parSlice);
+        setTemp(0, temp1);
+        setTemp(1, temp2);
+    }
+
+    MParNew *mir() const {
+        return mir_->toParNew();
+    }
+
+    const LAllocation *parSlice() {
+        return getOperand(0);
+    }
+
+    const LAllocation *getTemp0() {
+        return getTemp(0)->output();
+    }
+
+    const LAllocation *getTemp1() {
+        return getTemp(1)->output();
+    }
+};
+
+class LParNewDenseArray : public LCallInstructionHelper<1, 2, 3>
+{
+  public:
+    LIR_HEADER(ParNewDenseArray);
+
+    LParNewDenseArray(const LAllocation &parSlice,
+                      const LAllocation &length,
+                      const LDefinition &temp1,
+                      const LDefinition &temp2,
+                      const LDefinition &temp3) {
+        setOperand(0, parSlice);
+        setOperand(1, length);
+        setTemp(0, temp1);
+        setTemp(1, temp2);
+        setTemp(2, temp3);
+    }
+
+    MParNewDenseArray *mir() const {
+        return mir_->toParNewDenseArray();
+    }
+
+    const LAllocation *parSlice() {
+        return getOperand(0);
+    }
+
+    const LAllocation *length() {
+        return getOperand(1);
+    }
+
+    const LAllocation *getTemp0() {
+        return getTemp(0)->output();
+    }
+
+    const LAllocation *getTemp1() {
+        return getTemp(1)->output();
+    }
+
+    const LAllocation *getTemp2() {
+        return getTemp(2)->output();
+    }
+};
+
 // Allocates a new DeclEnvObject.
 //
 // This instruction generates two possible instruction sets:
 //   (1) An inline allocation of the call object is attempted.
 //   (2) Otherwise, a callVM create a new object.
 //
 class LNewDeclEnvObject : public LInstructionHelper<1, 0, 0>
 {
@@ -306,16 +389,74 @@ class LNewCallObject : public LInstructi
     const LAllocation *slots() {
         return getOperand(0);
     }
     MNewCallObject *mir() const {
         return mir_->toNewCallObject();
     }
 };
 
+class LParNewCallObject : public LInstructionHelper<1, 2, 2>
+{
+    LParNewCallObject(const LAllocation &parSlice,
+                      const LAllocation &slots,
+                      const LDefinition &temp1,
+                      const LDefinition &temp2) {
+        setOperand(0, parSlice);
+        setOperand(1, slots);
+        setTemp(0, temp1);
+        setTemp(1, temp2);
+    }
+
+public:
+    LIR_HEADER(ParNewCallObject);
+
+    static LParNewCallObject *NewWithSlots(const LAllocation &parSlice,
+                                           const LAllocation &slots,
+                                           const LDefinition &temp1,
+                                           const LDefinition &temp2) {
+        return new LParNewCallObject(parSlice, slots, temp1, temp2);
+    }
+
+    static LParNewCallObject *NewSansSlots(const LAllocation &parSlice,
+                                           const LDefinition &temp1,
+                                           const LDefinition &temp2) {
+        LAllocation slots = LConstantIndex::Bogus();
+        return new LParNewCallObject(parSlice, slots, temp1, temp2);
+    }
+
+    const LAllocation *parSlice() {
+        return getOperand(0);
+    }
+
+    const LAllocation *slots() {
+        return getOperand(1);
+    }
+
+    const bool hasDynamicSlots() {
+        // TO INVESTIGATE: Felix tried using isRegister() method here,
+        // but for useFixed(_, CallTempN), isRegister() is false (and
+        // isUse() is true).  So for now ignore that and try to match
+        // the LConstantIndex::Bogus() generated above instead.
+        return slots() && ! slots()->isConstant();
+    }
+
+    const MParNewCallObject *mir() const {
+        return mir_->toParNewCallObject();
+    }
+
+    const LAllocation *getTemp0() {
+        return getTemp(0)->output();
+    }
+
+    const LAllocation *getTemp1() {
+        return getTemp(1)->output();
+    }
+};
+
 class LNewStringObject : public LInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(NewStringObject)
 
     LNewStringObject(const LAllocation &input, const LDefinition &temp) {
         setOperand(0, input);
         setTemp(0, temp);
@@ -327,16 +468,22 @@ class LNewStringObject : public LInstruc
     const LDefinition *temp() {
         return getTemp(0);
     }
     MNewStringObject *mir() const {
         return mir_->toNewStringObject();
     }
 };
 
+class LParBailout : public LInstructionHelper<0, 0, 0>
+{
+  public:
+    LIR_HEADER(ParBailout);
+};
+
 // Takes in an Object and a Value.
 class LInitProp : public LCallInstructionHelper<0, 1 + BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(InitProp)
 
     LInitProp(const LAllocation &object) {
         setOperand(0, object);
@@ -366,16 +513,58 @@ class LCheckOverRecursed : public LInstr
         setTemp(0, limitreg);
     }
 
     const LAllocation *limitTemp() {
         return getTemp(0)->output();
     }
 };
 
+class LParCheckOverRecursed : public LInstructionHelper<0, 1, 1>
+{
+  public:
+    LIR_HEADER(ParCheckOverRecursed);
+
+    LParCheckOverRecursed(const LAllocation &parSlice,
+                          const LDefinition &tempReg)
+    {
+        setOperand(0, parSlice);
+        setTemp(0, tempReg);
+    }
+
+    const LAllocation *parSlice() {
+        return getOperand(0);
+    }
+
+    const LDefinition *getTempReg() {
+        return getTemp(0);
+    }
+};
+
+class LParCheckInterrupt : public LInstructionHelper<0, 1, 1>
+{
+  public:
+    LIR_HEADER(ParCheckInterrupt);
+
+    LParCheckInterrupt(const LAllocation &parSlice,
+                       const LDefinition &tempReg)
+    {
+        setOperand(0, parSlice);
+        setTemp(0, tempReg);
+    }
+
+    const LAllocation *parSlice() {
+        return getOperand(0);
+    }
+
+    const LDefinition *getTempReg() {
+        return getTemp(0);
+    }
+};
+
 class LDefVar : public LCallInstructionHelper<0, 1, 0>
 {
   public:
     LIR_HEADER(DefVar)
 
     LDefVar(const LAllocation &scopeChain)
     {
         setOperand(0, scopeChain);
@@ -1141,16 +1330,37 @@ class LCompareStrictS : public LInstruct
     const LDefinition *temp1() {
         return getTemp(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
+class LParCompareS : public LCallInstructionHelper<1, 2, 0>
+{
+  public:
+    LIR_HEADER(ParCompareS);
+
+    LParCompareS(const LAllocation &left, const LAllocation &right) {
+        setOperand(0, left);
+        setOperand(1, right);
+    }
+
+    const LAllocation *left() {
+        return getOperand(0);
+    }
+    const LAllocation *right() {
+        return getOperand(1);
+    }
+    MCompare *mir() {
+        return mir_->toCompare();
+    }
+};
+
 // Used for strict-equality comparisons where one side is a boolean
 // and the other is a value. Note that CompareI is used to compare
 // two booleans.
 class LCompareB : public LInstructionHelper<1, BOX_PIECES + 1, 0>
 {
   public:
     LIR_HEADER(CompareB)
 
@@ -2093,16 +2303,47 @@ class LLambda : public LInstructionHelpe
     const LAllocation *scopeChain() {
         return getOperand(0);
     }
     const MLambda *mir() const {
         return mir_->toLambda();
     }
 };
 
+class LParLambda : public LInstructionHelper<1, 2, 2>
+{
+  public:
+    LIR_HEADER(ParLambda);
+
+    LParLambda(const LAllocation &parSlice,
+               const LAllocation &scopeChain,
+               const LDefinition &temp1,
+               const LDefinition &temp2) {
+        setOperand(0, parSlice);
+        setOperand(1, scopeChain);
+        setTemp(0, temp1);
+        setTemp(1, temp2);
+    }
+    const LAllocation *parSlice() {
+        return getOperand(0);
+    }
+    const LAllocation *scopeChain() {
+        return getOperand(1);
+    }
+    const MParLambda *mir() const {
+        return mir_->toParLambda();
+    }
+    const LAllocation *getTemp0() {
+        return getTemp(0)->output();
+    }
+    const LAllocation *getTemp1() {
+        return getTemp(1)->output();
+    }
+};
+
 // Determines the implicit |this| value for function calls.
 class LImplicitThis : public LInstructionHelper<BOX_PIECES, 1, 0>
 {
   public:
     LIR_HEADER(ImplicitThis)
     BOX_OUTPUT_ACCESSORS()
 
     LImplicitThis(const LAllocation &callee) {
@@ -3071,16 +3312,30 @@ class LFunctionEnvironment : public LIns
     LFunctionEnvironment(const LAllocation &function) {
         setOperand(0, function);
     }
     const LAllocation *function() {
         return getOperand(0);
     }
 };
 
+class LParSlice : public LCallInstructionHelper<1, 0, 1>
+{
+  public:
+    LIR_HEADER(ParSlice);
+
+    LParSlice(const LDefinition &temp1) {
+        setTemp(0, temp1);
+    }
+
+    const LAllocation *getTempReg() {
+        return getTemp(0)->output();
+    }
+};
+
 class LCallGetProperty : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(CallGetProperty)
 
     static const size_t Value = 0;
 
     MCallGetProperty *mir() const {
@@ -3320,16 +3575,58 @@ class LGetArgument : public LInstruction
     LGetArgument(const LAllocation &index) {
         setOperand(0, index);
     }
     const LAllocation *index() {
         return getOperand(0);
     }
 };
 
+class LParWriteGuard : public LCallInstructionHelper<0, 2, 1>
+{
+  public:
+    LIR_HEADER(ParWriteGuard);
+
+    LParWriteGuard(const LAllocation &parSlice,
+                   const LAllocation &object,
+                   const LDefinition &temp1) {
+        setOperand(0, parSlice);
+        setOperand(1, object);
+        setTemp(0, temp1);
+    }
+
+    bool isCall() const {
+        return true;
+    }
+
+    const LAllocation *parSlice() {
+        return getOperand(0);
+    }
+
+    const LAllocation *object() {
+        return getOperand(1);
+    }
+
+    const LAllocation *getTempReg() {
+        return getTemp(0)->output();
+    }
+};
+
+class LParDump : public LCallInstructionHelper<0, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(ParDump);
+
+    static const size_t Value = 0;
+
+    const LAllocation *value() {
+        return getOperand(0);
+    }
+};
+
 // Guard that a value is in a TypeSet.
 class LTypeBarrier : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1>
 {
   public:
     LIR_HEADER(TypeBarrier)
     BOX_OUTPUT_ACCESSORS()
 
     LTypeBarrier(const LDefinition &temp) {
--- a/js/src/ion/LIR.h
+++ b/js/src/ion/LIR.h
@@ -541,16 +541,18 @@ class LDefinition
 #endif
           case MIRType_Slots:
           case MIRType_Elements:
             // When we begin allocating slots vectors from the GC, this will
             // need to change to ::OBJECT.
             return LDefinition::GENERAL;
           case MIRType_StackFrame:
             return LDefinition::GENERAL;
+          case MIRType_ForkJoinSlice:
+            return LDefinition::GENERAL;
           default:
             JS_NOT_REACHED("unexpected type");
             return LDefinition::GENERAL;
         }
     }
 };
 
 // Forward declarations of LIR types.
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -17,24 +17,30 @@
     _(Pointer)                      \
     _(Double)                       \
     _(Value)                        \
     _(Parameter)                    \
     _(Callee)                       \
     _(TableSwitch)                  \
     _(TableSwitchV)                 \
     _(Goto)                         \
+    _(NewParallelArray)             \
     _(NewArray)                     \
     _(NewObject)                    \
     _(NewSlots)                     \
     _(NewDeclEnvObject)             \
     _(NewCallObject)                \
     _(NewStringObject)              \
+    _(ParNew)                       \
+    _(ParNewDenseArray)             \
+    _(ParNewCallObject)             \
+    _(ParBailout)                   \
     _(InitProp)                     \
     _(CheckOverRecursed)            \
+    _(ParCheckOverRecursed)         \
     _(RecompileCheck)               \
     _(DefVar)                       \
     _(DefFun)                       \
     _(CallKnown)                    \
     _(CallGeneric)                  \
     _(CallNative)                   \
     _(ApplyArgsGeneric)             \
     _(StackArgT)                    \
@@ -58,16 +64,17 @@
     _(TestOAndBranch)               \
     _(PolyInlineDispatch)           \
     _(Compare)                      \
     _(CompareAndBranch)             \
     _(CompareD)                     \
     _(CompareDAndBranch)            \
     _(CompareS)                     \
     _(CompareStrictS)               \
+    _(ParCompareS)                  \
     _(CompareB)                     \
     _(CompareBAndBranch)            \
     _(CompareV)                     \
     _(CompareVAndBranch)            \
     _(CompareVM)                    \
     _(IsNullOrLikeUndefined)        \
     _(IsNullOrLikeUndefinedAndBranch)\
     _(EmulatesUndefined)            \
@@ -104,26 +111,29 @@
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
     _(RegExp)                       \
     _(RegExpTest)                   \
     _(Lambda)                       \
     _(LambdaForSingleton)           \
+    _(ParLambda)                    \
     _(ImplicitThis)                 \
     _(Slots)                        \
     _(Elements)                     \
     _(ConvertElementsToDoubles)     \
     _(LoadSlotV)                    \
     _(LoadSlotT)                    \
     _(StoreSlotV)                   \
     _(StoreSlotT)                   \
     _(GuardShape)                   \
     _(GuardClass)                   \
+    _(ParWriteGuard)                \
+    _(ParDump)                      \
     _(TypeBarrier)                  \
     _(MonitorTypes)                 \
     _(InitializedLength)            \
     _(SetInitializedLength)         \
     _(BoundsCheck)                  \
     _(BoundsCheckRange)             \
     _(BoundsCheckLower)             \
     _(LoadElementV)                 \
@@ -144,16 +154,17 @@
     _(ClampIToUint8)                \
     _(ClampDToUint8)                \
     _(ClampVToUint8)                \
     _(LoadFixedSlotV)               \
     _(LoadFixedSlotT)               \
     _(StoreFixedSlotV)              \
     _(StoreFixedSlotT)              \
     _(FunctionEnvironment)          \
+    _(ParSlice)                     \
     _(GetPropertyCacheV)            \
     _(GetPropertyCacheT)            \
     _(GetElementCacheV)             \
     _(BindNameCache)                \
     _(CallGetProperty)              \
     _(GetNameCache)                 \
     _(CallGetIntrinsicValue)        \
     _(CallsiteCloneCache)           \
@@ -179,16 +190,17 @@
     _(Floor)                        \
     _(Round)                        \
     _(In)                           \
     _(InArray)                      \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
+    _(ParCheckInterrupt)            \
     _(FunctionBoundary)             \
     _(GetDOMProperty)               \
     _(SetDOMProperty)               \
     _(CallDOMNative)
 
 #if defined(JS_CPU_X86)
 # include "x86/LOpcodes-x86.h"
 #elif defined(JS_CPU_X64)
--- a/js/src/ion/LinearScan.cpp
+++ b/js/src/ion/LinearScan.cpp
@@ -624,16 +624,17 @@ LinearScanAllocator::splitBlockingInterv
 {
     JS_ASSERT(allocation.isRegister());
 
     // Split current before the next fixed use.
     LiveInterval *fixed = fixedIntervals[allocation.toRegister().code()];
     if (fixed->numRanges() > 0) {
         CodePosition fixedPos = current->intersect(fixed);
         if (fixedPos != CodePosition::MIN) {
+            JS_ASSERT(fixedPos > current->start());
             JS_ASSERT(fixedPos < current->end());
             if (!splitInterval(current, fixedPos))
                 return false;
         }
     }
 
     // Split the blocking interval if it exists.
     for (IntervalIterator i(active.begin()); i != active.end(); i++) {
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -109,16 +109,29 @@ LIRGenerator::visitCheckOverRecursed(MCh
         return false;
     if (!assignSafepoint(lir, ins))
         return false;
 
     return true;
 }
 
 bool
+LIRGenerator::visitParCheckOverRecursed(MParCheckOverRecursed *ins)
+{
+    LParCheckOverRecursed *lir = new LParCheckOverRecursed(
+        useRegister(ins->parSlice()),
+        temp());
+    if (!add(lir))
+        return false;
+    if (!assignSafepoint(lir, ins))
+        return false;
+    return true;
+}
+
+bool
 LIRGenerator::visitDefVar(MDefVar *ins)
 {
     LDefVar *lir = new LDefVar(useRegisterAtStart(ins->scopeChain()));
     if (!add(lir, ins))
         return false;
     if (!assignSafepoint(lir, ins))
         return false;
 
@@ -139,16 +152,23 @@ LIRGenerator::visitNewSlots(MNewSlots *i
     LNewSlots *lir = new LNewSlots(tempFixed(CallTempReg0), tempFixed(CallTempReg1),
                                    tempFixed(CallTempReg2));
     if (!assignSnapshot(lir))
         return false;
     return defineReturn(lir, ins);
 }
 
 bool
+LIRGenerator::visitNewParallelArray(MNewParallelArray *ins)
+{
+    LNewParallelArray *lir = new LNewParallelArray();
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitNewArray(MNewArray *ins)
 {
     LNewArray *lir = new LNewArray();
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitNewObject(MNewObject *ins)
@@ -179,25 +199,51 @@ LIRGenerator::visitNewCallObject(MNewCal
 
     if (!assignSafepoint(lir, ins))
         return false;
 
     return true;
 }
 
 bool
+LIRGenerator::visitParNewCallObject(MParNewCallObject *ins)
+{
+    const LAllocation &parThreadContext = useRegister(ins->parSlice());
+    const LDefinition &temp1 = temp();
+    const LDefinition &temp2 = temp();
+
+    LParNewCallObject *lir;
+    if (ins->slots()->type() == MIRType_Slots) {
+        const LAllocation &slots = useRegister(ins->slots());
+        lir = LParNewCallObject::NewWithSlots(parThreadContext, slots,
+                                              temp1, temp2);
+    } else {
+        lir = LParNewCallObject::NewSansSlots(parThreadContext, temp1, temp2);
+    }
+
+    return define(lir, ins);
+}
+
+bool
 LIRGenerator::visitNewStringObject(MNewStringObject *ins)
 {
     JS_ASSERT(ins->input()->type() == MIRType_String);
 
     LNewStringObject *lir = new LNewStringObject(useRegister(ins->input()), temp());
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitParBailout(MParBailout *ins)
+{
+    LParBailout *lir = new LParBailout();
+    return add(lir, ins);
+}
+
+bool
 LIRGenerator::visitInitProp(MInitProp *ins)
 {
     LInitProp *lir = new LInitProp(useRegisterAtStart(ins->getObject()));
     if (!useBoxAtStart(lir, LInitProp::ValueIndex, ins->getValue()))
         return false;
 
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
@@ -575,20 +621,34 @@ LIRGenerator::visitCompare(MCompare *com
     bool result;
     if (comp->tryFold(&result))
         return define(new LInteger(result), comp);
 
     // Move below the emitAtUses call if we ever implement
     // LCompareSAndBranch. Doing this now wouldn't be wrong, but doesn't
     // make sense and avoids confusion.
     if (comp->compareType() == MCompare::Compare_String) {
-        LCompareS *lir = new LCompareS(useRegister(left), useRegister(right), temp());
-        if (!define(lir, comp))
-            return false;
-        return assignSafepoint(lir, comp);
+        switch (comp->block()->info().executionMode()) {
+          case SequentialExecution:
+          {
+              LCompareS *lir = new LCompareS(useRegister(left), useRegister(right), temp());
+              if (!define(lir, comp))
+                  return false;
+              return assignSafepoint(lir, comp);
+          }
+
+          case ParallelExecution:
+          {
+              LParCompareS *lir = new LParCompareS(useFixed(left, CallTempReg0),
+                                                   useFixed(right, CallTempReg1));
+              return defineReturn(lir, comp);
+          }
+        }
+
+        JS_NOT_REACHED("Unexpected execution mode");
     }
 
     // Strict compare between value and string
     if (comp->compareType() == MCompare::Compare_StrictString) {
         JS_ASSERT(left->type() == MIRType_Value);
         JS_ASSERT(right->type() == MIRType_String);
 
         LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), temp());
@@ -1378,16 +1438,27 @@ LIRGenerator::visitLambda(MLambda *ins)
         return defineReturn(lir, ins) && assignSafepoint(lir, ins);
     }
 
     LLambda *lir = new LLambda(useRegister(ins->scopeChain()));
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitParLambda(MParLambda *ins)
+{
+    JS_ASSERT(!ins->fun()->hasSingletonType());
+    JS_ASSERT(!types::UseNewTypeForClone(ins->fun()));
+    LParLambda *lir = new LParLambda(useRegister(ins->parSlice()),
+                                     useRegister(ins->scopeChain()),
+                                     temp(), temp());
+    return define(lir, ins);
+}
+
+bool
 LIRGenerator::visitImplicitThis(MImplicitThis *ins)
 {
     JS_ASSERT(ins->callee()->type() == MIRType_Object);
 
     LImplicitThis *lir = new LImplicitThis(useRegister(ins->callee()));
     return assignSnapshot(lir) && defineBox(lir, ins);
 }
 
@@ -1435,16 +1506,72 @@ LIRGenerator::visitLoadSlot(MLoadSlot *i
 
 bool
 LIRGenerator::visitFunctionEnvironment(MFunctionEnvironment *ins)
 {
     return define(new LFunctionEnvironment(useRegisterAtStart(ins->function())), ins);
 }
 
 bool
+LIRGenerator::visitParSlice(MParSlice *ins)
+{
+    LParSlice *lir = new LParSlice(tempFixed(CallTempReg0));
+    return defineReturn(lir, ins);
+}
+
+bool
+LIRGenerator::visitParWriteGuard(MParWriteGuard *ins)
+{
+    return add(new LParWriteGuard(useFixed(ins->parSlice(), CallTempReg0),
+                                  useFixed(ins->object(), CallTempReg1),
+                                  tempFixed(CallTempReg2)));
+}
+
+bool
+LIRGenerator::visitParCheckInterrupt(MParCheckInterrupt *ins)
+{
+    LParCheckInterrupt *lir = new LParCheckInterrupt(
+        useRegister(ins->parSlice()),
+        temp());
+    if (!add(lir))
+        return false;
+    if (!assignSafepoint(lir, ins))
+        return false;
+    return true;
+}
+
+bool
+LIRGenerator::visitParDump(MParDump *ins)
+{
+    LParDump *lir = new LParDump();
+    useBoxFixed(lir, LParDump::Value, ins->value(), CallTempReg0, CallTempReg1);
+    return add(lir);
+}
+
+bool
+LIRGenerator::visitParNew(MParNew *ins)
+{
+    LParNew *lir = new LParNew(useRegister(ins->parSlice()),
+                               temp(), temp());
+    return define(lir, ins);
+}
+
+bool
+LIRGenerator::visitParNewDenseArray(MParNewDenseArray *ins)
+{
+    LParNewDenseArray *lir = new LParNewDenseArray(
+        useFixed(ins->parSlice(), CallTempReg0),
+        useFixed(ins->length(), CallTempReg1),
+        tempFixed(CallTempReg2),
+        tempFixed(CallTempReg3),
+        tempFixed(CallTempReg4));
+    return defineReturn(lir, ins);
+}
+
+bool
 LIRGenerator::visitStoreSlot(MStoreSlot *ins)
 {
     LInstruction *lir;
 
     switch (ins->value()->type()) {
       case MIRType_Value:
         lir = new LStoreSlotV(useRegister(ins->slots()));
         if (!useBox(lir, LStoreSlotV::Value, ins->value()))
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -74,23 +74,29 @@ class LIRGenerator : public LIRGenerator
 
     // Visitor hooks are explicit, to give CPU-specific versions a chance to
     // intercept without a bunch of explicit gunk in the .cpp.
     bool visitParameter(MParameter *param);
     bool visitCallee(MCallee *callee);
     bool visitGoto(MGoto *ins);
     bool visitTableSwitch(MTableSwitch *tableswitch);
     bool visitNewSlots(MNewSlots *ins);
+    bool visitNewParallelArray(MNewParallelArray *ins);
     bool visitNewArray(MNewArray *ins);
     bool visitNewObject(MNewObject *ins);
     bool visitNewDeclEnvObject(MNewDeclEnvObject *ins);
     bool visitNewCallObject(MNewCallObject *ins);
     bool visitNewStringObject(MNewStringObject *ins);
+    bool visitParNew(MParNew *ins);
+    bool visitParNewCallObject(MParNewCallObject *ins);
+    bool visitParNewDenseArray(MParNewDenseArray *ins);
+    bool visitParBailout(MParBailout *ins);
     bool visitInitProp(MInitProp *ins);
     bool visitCheckOverRecursed(MCheckOverRecursed *ins);
+    bool visitParCheckOverRecursed(MParCheckOverRecursed *ins);
     bool visitDefVar(MDefVar *ins);
     bool visitDefFun(MDefFun *ins);
     bool visitPrepareCall(MPrepareCall *ins);
     bool visitPassArg(MPassArg *arg);
     bool visitCreateThisWithTemplate(MCreateThisWithTemplate *ins);
     bool visitCreateThisWithProto(MCreateThisWithProto *ins);
     bool visitCreateThis(MCreateThis *ins);
     bool visitReturnFromCtor(MReturnFromCtor *ins);
@@ -131,23 +137,28 @@ class LIRGenerator : public LIRGenerator
     bool visitOsrScopeChain(MOsrScopeChain *object);
     bool visitToDouble(MToDouble *convert);
     bool visitToInt32(MToInt32 *convert);
     bool visitTruncateToInt32(MTruncateToInt32 *truncate);
     bool visitToString(MToString *convert);
     bool visitRegExp(MRegExp *ins);
     bool visitRegExpTest(MRegExpTest *ins);
     bool visitLambda(MLambda *ins);
+    bool visitParLambda(MParLambda *ins);
     bool visitImplicitThis(MImplicitThis *ins);
     bool visitSlots(MSlots *ins);
     bool visitElements(MElements *ins);
     bool visitConstantElements(MConstantElements *ins);
     bool visitConvertElementsToDoubles(MConvertElementsToDoubles *ins);
     bool visitLoadSlot(MLoadSlot *ins);
     bool visitFunctionEnvironment(MFunctionEnvironment *ins);
+    bool visitParSlice(MParSlice *ins);
+    bool visitParWriteGuard(MParWriteGuard *ins);
+    bool visitParCheckInterrupt(MParCheckInterrupt *ins);
+    bool visitParDump(MParDump *ins);
     bool visitStoreSlot(MStoreSlot *ins);
     bool visitTypeBarrier(MTypeBarrier *ins);
     bool visitMonitorTypes(MMonitorTypes *ins);
     bool visitArrayLength(MArrayLength *ins);
     bool visitTypedArrayLength(MTypedArrayLength *ins);
     bool visitTypedArrayElements(MTypedArrayElements *ins);
     bool visitInitializedLength(MInitializedLength *ins);
     bool visitSetInitializedLength(MSetInitializedLength *ins);
--- a/js/src/ion/MCallOptimize.cpp
+++ b/js/src/ion/MCallOptimize.cpp
@@ -2,16 +2,18 @@
  * vim: set ts=4 sw=4 et tw=99:
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jslibmath.h"
 #include "jsmath.h"
+#include "builtin/ParallelArray.h"
+#include "builtin/TestingFunctions.h"
 
 #include "MIR.h"
 #include "MIRGraph.h"
 #include "IonBuilder.h"
 
 #include "vm/StringObject-inl.h"
 
 namespace js {
@@ -71,16 +73,32 @@ IonBuilder::inlineNativeCall(CallInfo &c
         return inlineStrCharAt(callInfo);
 
     // RegExp natives.
     if (native == regexp_exec && !CallResultEscapes(pc))
         return inlineRegExpTest(callInfo);
     if (native == regexp_test)
         return inlineRegExpTest(callInfo);
 
+    // Parallel Array
+    if (native == intrinsic_UnsafeSetElement)
+        return inlineUnsafeSetElement(callInfo);
+    if (native == testingFunc_inParallelSection)
+        return inlineForceSequentialOrInParallelSection(callInfo);
+    if (native == intrinsic_NewDenseArray)
+        return inlineNewDenseArray(callInfo);
+
+    // Self-hosting
+    if (native == intrinsic_ThrowError)
+        return inlineThrowError(callInfo);
+#ifdef DEBUG
+    if (native == intrinsic_Dump)
+        return inlineDump(callInfo);
+#endif
+
     return InliningStatus_NotInlined;
 }
 
 types::StackTypeSet *
 IonBuilder::getInlineReturnTypeSet()
 {
     types::StackTypeSet *barrier;
     types::StackTypeSet *returnTypes = oracle->returnTypeSet(script(), pc, &barrier);
@@ -841,10 +859,291 @@ IonBuilder::inlineRegExpTest(CallInfo &c
     current->add(match);
     current->push(match);
     if (!resumeAfter(match))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
+IonBuilder::InliningStatus
+IonBuilder::inlineUnsafeSetElement(CallInfo &callInfo)
+{
+    uint32_t argc = callInfo.argc();
+    if (argc < 3 || (argc % 3) != 0 || callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    /* Important:
+     *
+     * Here we inline each of the stores resulting from a call to
+     * %UnsafeSetElement().  It is essential that these stores occur
+     * atomically and cannot be interrupted by a stack or recursion
+     * check.  If this is not true, race conditions can occur.
+     */
+
+    for (uint32_t base = 0; base < argc; base += 3) {
+        uint32_t arri = base + 1;
+        uint32_t idxi = base + 2;
+
+        types::StackTypeSet *obj = getInlineArgTypeSet(callInfo, arri);
+        types::StackTypeSet *id = getInlineArgTypeSet(callInfo, idxi);
+
+        int arrayType;
+        if (!oracle->elementAccessIsDenseNative(obj, id) &&
+            !oracle->elementAccessIsTypedArray(obj, id, &arrayType))
+        {
+            return InliningStatus_NotInlined;
+        }
+    }
+
+    callInfo.unwrapArgs();
+
+    // Push the result first so that the stack depth matches up for
+    // the potential bailouts that will occur in the stores below.
+    MConstant *udef = MConstant::New(UndefinedValue());
+    current->add(udef);
+    current->push(udef);
+
+    for (uint32_t base = 0; base < argc; base += 3) {
+        uint32_t arri = base + 1;
+        uint32_t idxi = base + 2;
+
+        types::StackTypeSet *obj = getInlineArgTypeSet(callInfo, arri);
+        types::StackTypeSet *id = getInlineArgTypeSet(callInfo, idxi);
+
+        if (oracle->elementAccessIsDenseNative(obj, id)) {
+            if (!inlineUnsafeSetDenseArrayElement(callInfo, base))
+                return InliningStatus_Error;
+            continue;
+        }
+
+        int arrayType;
+        if (oracle->elementAccessIsTypedArray(obj, id, &arrayType)) {
+            if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType))
+                return InliningStatus_Error;
+            continue;
+        }
+
+        JS_NOT_REACHED("Element access not dense array nor typed array");
+    }
+
+    return InliningStatus_Inlined;
+}
+
+bool
+IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base)
+{
+    // Note: we do not check the conditions that are asserted as true
+    // in intrinsic_UnsafeSetElement():
+    // - arr is a dense array
+    // - idx < initialized length
+    // Furthermore, note that inference should be propagating
+    // the type of the value to the JSID_VOID property of the array.
+
+    uint32_t arri = base + 1;
+    uint32_t idxi = base + 2;
+    uint32_t elemi = base + 3;
+
+    MElements *elements = MElements::New(callInfo.getArg(arri));
+    current->add(elements);
+
+    MToInt32 *id = MToInt32::New(callInfo.getArg(idxi));
+    current->add(id);
+
+    // We disable the hole check for this store.  This implies that if
+    // there were setters on the prototype, they would not be invoked.
+    // But this is actually the desired behavior.
+
+    MStoreElement *store = MStoreElement::New(elements, id,
+                                              callInfo.getArg(elemi),
+                                              /* needsHoleCheck = */ false);
+    store->setRacy();
+
+    current->add(store);
+
+    if (!resumeAfter(store))
+        return false;
+
+    return true;
+}
+
+bool
+IonBuilder::inlineUnsafeSetTypedArrayElement(CallInfo &callInfo,
+                                             uint32_t base,
+                                             int arrayType)
+{
+    // Note: we do not check the conditions that are asserted as true
+    // in intrinsic_UnsafeSetElement():
+    // - arr is a typed array
+    // - idx < length
+
+    uint32_t arri = base + 1;
+    uint32_t idxi = base + 2;
+    uint32_t elemi = base + 3;
+
+    MInstruction *elements = getTypedArrayElements(callInfo.getArg(arri));
+    current->add(elements);
+
+    MToInt32 *id = MToInt32::New(callInfo.getArg(idxi));
+    current->add(id);
+
+    MDefinition *value = callInfo.getArg(elemi);
+    if (arrayType == TypedArray::TYPE_UINT8_CLAMPED) {
+        value = MClampToUint8::New(value);
+        current->add(value->toInstruction());
+    }
+
+    MStoreTypedArrayElement *store = MStoreTypedArrayElement::New(elements, id, value, arrayType);
+    store->setRacy();
+
+    current->add(store);
+
+    if (!resumeAfter(store))
+        return false;
+
+    return true;
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo)
+{
+    if (callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    ExecutionMode executionMode = info().executionMode();
+    switch (executionMode) {
+      case SequentialExecution:
+        // In sequential mode, leave as is, because we'd have to
+        // access the "in warmup" flag of the runtime.
+        return InliningStatus_NotInlined;
+
+      case ParallelExecution:
+        // During Parallel Exec, we always force sequential, so
+        // replace with true.  This permits UCE to eliminate the
+        // entire path as dead, which is important.
+        callInfo.unwrapArgs();
+        MConstant *ins = MConstant::New(BooleanValue(true));
+        current->add(ins);
+        current->push(ins);
+        return InliningStatus_Inlined;
+    }
+
+    JS_NOT_REACHED("Invalid execution mode");
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineNewDenseArray(CallInfo &callInfo)
+{
+    if (callInfo.constructing() || callInfo.argc() != 1)
+        return InliningStatus_NotInlined;
+
+    // For now, in seq. mode we just call the C function.  In
+    // par. mode we use inlined MIR.
+    ExecutionMode executionMode = info().executionMode();
+    switch (executionMode) {
+      case SequentialExecution:
+        return inlineNewDenseArrayForSequentialExecution(callInfo);
+      case ParallelExecution:
+        return inlineNewDenseArrayForParallelExecution(callInfo);
+    }
+
+    JS_NOT_REACHED("unknown ExecutionMode");
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo)
+{
+    // not yet implemented; in seq. mode the C function is not so bad
+    return InliningStatus_NotInlined;
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineNewDenseArrayForParallelExecution(CallInfo &callInfo)
+{
+    // Create the new parallel array object.  Parallel arrays have specially
+    // constructed type objects, so we can only perform the inlining if we
+    // already have one of these type objects.
+    types::StackTypeSet *returnTypes = getInlineReturnTypeSet();
+    if (returnTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT)
+        return InliningStatus_NotInlined;
+    if (returnTypes->getObjectCount() != 1)
+        return InliningStatus_NotInlined;
+    types::TypeObject *typeObject = returnTypes->getTypeObject(0);
+
+    RootedObject templateObject(cx, NewDenseAllocatedArray(cx, 0));
+    if (!templateObject)
+        return InliningStatus_Error;
+    templateObject->setType(typeObject);
+
+    MParNewDenseArray *newObject = new MParNewDenseArray(graph().parSlice(),
+                                                         callInfo.getArg(1),
+                                                         templateObject);
+    current->add(newObject);
+    current->push(newObject);
+
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineThrowError(CallInfo &callInfo)
+{
+    // In Parallel Execution, convert %ThrowError() into a bailout.
+
+    if (callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    ExecutionMode executionMode = info().executionMode();
+    switch (executionMode) {
+      case SequentialExecution:
+        return InliningStatus_NotInlined;
+      case ParallelExecution:
+        break;
+    }
+
+    callInfo.unwrapArgs();
+
+    MParBailout *bailout = new MParBailout();
+    if (!bailout)
+        return InliningStatus_Error;
+    current->end(bailout);
+
+    current = newBlock(pc);
+    if (!current)
+        return InliningStatus_Error;
+
+    MConstant *udef = MConstant::New(UndefinedValue());
+    current->add(udef);
+    current->push(udef);
+
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineDump(CallInfo &callInfo)
+{
+    // In Parallel Execution, call ParDump.  We just need a debugging
+    // aid!
+
+    if (callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    ExecutionMode executionMode = info().executionMode();
+    switch (executionMode) {
+      case SequentialExecution:
+        return InliningStatus_NotInlined;
+      case ParallelExecution:
+        break;
+    }
+
+    callInfo.unwrapArgs();
+
+    MParDump *dump = new MParDump(callInfo.getArg(1));
+    current->add(dump);
+
+    MConstant *udef = MConstant::New(UndefinedValue());
+    current->add(udef);
+    current->push(udef);
+
+    return InliningStatus_Inlined;
+}
+
 } // namespace ion
 } // namespace js
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -1875,16 +1875,39 @@ MBeta::computeRange()
         IonSpew(IonSpew_Range, "Marking block for inst %d unexitable", id());
         block()->setEarlyAbort();
     } else {
         setRange(range);
     }
 }
 
 bool
+MNewObject::shouldUseVM() const
+{
+    return templateObject()->hasSingletonType() ||
+           templateObject()->hasDynamicSlots();
+}
+
+bool
+MNewArray::shouldUseVM() const
+{
+    JS_ASSERT(count() < JSObject::NELEMENTS_LIMIT);
+
+    size_t maxArraySlots =
+        gc::GetGCKindSlots(gc::FINALIZE_OBJECT_LAST) - ObjectElements::VALUES_PER_HEADER;
+
+    // Allocate space using the VMCall
+    // when mir hints it needs to get allocated immediatly,
+    // but only when data doesn't fit the available array slots.
+    bool allocating = isAllocating() && count() > maxArraySlots;
+
+    return templateObject()->hasSingletonType() || allocating;
+}
+
+bool
 MLoadFixedSlot::mightAlias(MDefinition *store)
 {
     if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot())
         return false;
     return true;
 }
 
 bool
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -305,16 +305,17 @@ class MDefinition : public MNode
         range_(),
         resultType_(MIRType_None),
         flags_(0),
         dependency_(NULL),
         trackedPc_(NULL)
     { }
 
     virtual Opcode op() const = 0;
+    virtual const char *opName() const = 0;
     void printName(FILE *fp);
     static void PrintOpcodeName(FILE *fp, Opcode op);
     virtual void printOpcode(FILE *fp);
 
     void setTrackedPc(jsbytecode *pc) {
         trackedPc_ = pc;
     }
 
@@ -574,16 +575,19 @@ class MInstruction
         return resumePoint_;
     }
 };
 
 #define INSTRUCTION_HEADER(opcode)                                          \
     Opcode op() const {                                                     \
         return MDefinition::Op_##opcode;                                    \
     }                                                                       \
+    const char *opName() const {                                            \
+        return #opcode;                                                     \
+    }                                                                       \
     bool accept(MInstructionVisitor *visitor) {                             \
         return visitor->visit##opcode(this);                                \
     }
 
 template <size_t Arity>
 class MAryInstruction : public MInstruction
 {
   protected:
@@ -605,16 +609,25 @@ class MAryInstruction : public MInstruct
     size_t numOperands() const {
         return Arity;
     }
 };
 
 class MNullaryInstruction : public MAryInstruction<0>
 { };
 
+class MUnaryInstruction : public MAryInstruction<1>
+{
+  protected:
+    MUnaryInstruction(MDefinition *ins)
+    {
+        setOperand(0, ins);
+    }
+};
+
 // Generates an LSnapshot without further effect.
 class MStart : public MNullaryInstruction
 {
   public:
     enum StartType {
         StartType_Default,
         StartType_Osr
     };
@@ -1063,16 +1076,38 @@ class MThrow
     TypePolicy *typePolicy() {
         return this;
     }
     virtual AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
+class MNewParallelArray : public MNullaryInstruction
+{
+    CompilerRootObject templateObject_;
+
+    MNewParallelArray(JSObject *templateObject)
+      : templateObject_(templateObject)
+    {
+        setResultType(MIRType_Object);
+    }
+
+  public:
+    INSTRUCTION_HEADER(NewParallelArray);
+
+    static MNewParallelArray *New(JSObject *templateObject) {
+        return new MNewParallelArray(templateObject);
+    }
+
+    JSObject *templateObject() const {
+        return templateObject_;
+    }
+};
+
 class MNewArray : public MNullaryInstruction
 {
   public:
     enum AllocatingBehaviour {
         NewArray_Allocating,
         NewArray_Unallocating
     };
 
@@ -1102,16 +1137,20 @@ class MNewArray : public MNullaryInstruc
     JSObject *templateObject() const {
         return templateObject_;
     }
 
     bool isAllocating() const {
         return allocating_ == NewArray_Allocating;
     }
 
+    // Returns true if the code generator should call through to the
+    // VM rather than the fast path.
+    bool shouldUseVM() const;
+
     // NewArray is marked as non-effectful because all our allocations are
     // either lazy when we are using "new Array(length)" or bounded by the
     // script or the stack size when we are using "new Array(...)" or "[...]"
     // notations.  So we might have to allocate the array twice if we bail
     // during the computation of the first element of the square braket
     // notation.
     virtual AliasSet getAliasSet() const {
         return AliasSet::None();
@@ -1130,21 +1169,64 @@ class MNewObject : public MNullaryInstru
 
   public:
     INSTRUCTION_HEADER(NewObject)
 
     static MNewObject *New(JSObject *templateObject) {
         return new MNewObject(templateObject);
     }
 
+    // Returns true if the code generator should call through to the
+    // VM rather than the fast path.
+    bool shouldUseVM() const;
+
     JSObject *templateObject() const {
         return templateObject_;
     }
 };
 
+// Could be allocating either a new array or a new object.
+class MParNew : public MUnaryInstruction
+{
+    CompilerRootObject templateObject_;
+
+  public:
+    INSTRUCTION_HEADER(ParNew);
+
+    MParNew(MDefinition *parSlice,
+            JSObject *templateObject)
+      : MUnaryInstruction(parSlice),
+        templateObject_(templateObject)
+    {
+        setResultType(MIRType_Object);
+    }
+
+    MDefinition *parSlice() const {
+        return getOperand(0);
+    }
+
+    JSObject *templateObject() const {
+        return templateObject_;
+    }
+};
+
+// Could be allocating either a new array or a new object.
+class MParBailout : public MAryControlInstruction<0, 0>
+{
+  public:
+    INSTRUCTION_HEADER(ParBailout);
+
+    MParBailout()
+      : MAryControlInstruction()
+    {
+        setResultType(MIRType_Undefined);
+        setGuard();
+    }
+};
+
 // Slow path for adding a property to an object without a known base.
 class MInitProp
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
 {
   public:
     CompilerRootPropertyName name_;
 
@@ -1355,25 +1437,16 @@ class MApplyArgs
         return getOperand(2);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 };
 
-class MUnaryInstruction : public MAryInstruction<1>
-{
-  protected:
-    MUnaryInstruction(MDefinition *ins)
-    {
-        setOperand(0, ins);
-    }
-};
-
 class MBinaryInstruction : public MAryInstruction<2>
 {
   protected:
     MBinaryInstruction(MDefinition *left, MDefinition *right)
     {
         setOperand(0, left);
         setOperand(1, right);
     }
@@ -3183,16 +3256,55 @@ class MOsrScopeChain : public MUnaryInst
 
 // Check the current frame for over-recursion past the global stack limit.
 class MCheckOverRecursed : public MNullaryInstruction
 {
   public:
     INSTRUCTION_HEADER(CheckOverRecursed)
 };
 
+// Check the current frame for over-recursion past the global stack limit.
+// Uses the per-thread recursion limit.
+class MParCheckOverRecursed : public MUnaryInstruction
+{
+  public:
+    INSTRUCTION_HEADER(ParCheckOverRecursed);
+
+    MParCheckOverRecursed(MDefinition *parForkJoinSlice)
+      : MUnaryInstruction(parForkJoinSlice)
+    {
+        setResultType(MIRType_None);
+        setGuard();
+        setMovable();
+    }
+
+    MDefinition *parSlice() const {
+        return getOperand(0);
+    }
+};
+
+// Check for an interrupt (or rendezvous) in parallel mode.
+class MParCheckInterrupt : public MUnaryInstruction
+{
+  public:
+    INSTRUCTION_HEADER(ParCheckInterrupt);
+
+    MParCheckInterrupt(MDefinition *parForkJoinSlice)
+      : MUnaryInstruction(parForkJoinSlice)
+    {
+        setResultType(MIRType_None);
+        setGuard();
+        setMovable();
+    }
+
+    MDefinition *parSlice() const {
+        return getOperand(0);
+    }
+};
+
 // Check the script's use count and trigger recompilation to inline
 // calls when the script becomes hot.
 class MRecompileCheck : public MNullaryInstruction
 {
     uint32_t minUses_;
 
     MRecompileCheck(uint32_t minUses)
       : minUses_(minUses)
@@ -3396,16 +3508,57 @@ class MLambda
     JSFunction *fun() const {
         return fun_;
     }
     TypePolicy *typePolicy() {
         return this;
     }
 };
 
+class MParLambda
+  : public MBinaryInstruction,
+    public SingleObjectPolicy
+{
+    CompilerRootFunction fun_;
+
+    MParLambda(MDefinition *parSlice,
+               MDefinition *scopeChain, JSFunction *fun)
+      : MBinaryInstruction(parSlice, scopeChain), fun_(fun)
+    {
+        setResultType(MIRType_Object);
+    }
+
+  public:
+    INSTRUCTION_HEADER(ParLambda);
+
+    static MParLambda *New(MDefinition *parSlice,
+                           MDefinition *scopeChain, JSFunction *fun) {
+        return new MParLambda(parSlice, scopeChain, fun);
+    }
+
+    static MParLambda *New(MDefinition *parSlice,
+                           MLambda *originalInstruction) {
+        return New(parSlice,
+                   originalInstruction->scopeChain(),
+                   originalInstruction->fun());
+    }
+
+    MDefinition *parSlice() const {
+        return getOperand(0);
+    }
+
+    MDefinition *scopeChain() const {
+        return getOperand(1);
+    }
+
+    JSFunction *fun() const {
+        return fun_;
+    }
+};
+
 // Determines the implicit |this| value for function calls.
 class MImplicitThis
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
     MImplicitThis(MDefinition *callee)
       : MUnaryInstruction(callee)
     {
@@ -3955,37 +4108,45 @@ class MLoadElementHole
         return AliasSet::Load(AliasSet::Element);
     }
 };
 
 class MStoreElementCommon
 {
     bool needsBarrier_;
     MIRType elementType_;
+    bool racy_; // if true, exempted from normal data race req. during par. exec.
 
   protected:
     MStoreElementCommon()
       : needsBarrier_(false),
-        elementType_(MIRType_Value)
+        elementType_(MIRType_Value),
+        racy_(false)
     { }
 
   public:
     MIRType elementType() const {
         return elementType_;
     }
     void setElementType(MIRType elementType) {
         JS_ASSERT(elementType != MIRType_None);
         elementType_ = elementType;
     }
     bool needsBarrier() const {
         return needsBarrier_;
     }
     void setNeedsBarrier() {
         needsBarrier_ = true;
     }
+    bool racy() const {
+        return racy_;
+    }
+    void setRacy() {
+        racy_ = true;
+    }
 };
 
 // Store a value to a dense array slots vector.
 class MStoreElement
   : public MAryInstruction<3>,
     public MStoreElementCommon,
     public SingleObjectPolicy
 {
@@ -4283,19 +4444,22 @@ class MLoadTypedArrayElementHole
 };
 
 class MStoreTypedArrayElement
   : public MTernaryInstruction,
     public StoreTypedArrayPolicy
 {
     int arrayType_;
 
+    // See note in MStoreElementCommon.
+    bool racy_;
+
     MStoreTypedArrayElement(MDefinition *elements, MDefinition *index, MDefinition *value,
                             int arrayType)
-      : MTernaryInstruction(elements, index, value), arrayType_(arrayType)
+      : MTernaryInstruction(elements, index, value), arrayType_(arrayType), racy_(false)
     {
         setResultType(MIRType_Value);
         setMovable();
         JS_ASSERT(elements->type() == MIRType_Elements);
         JS_ASSERT(index->type() == MIRType_Int32);
         JS_ASSERT(arrayType >= 0 && arrayType < TypedArray::TYPE_MAX);
     }
 
@@ -4329,16 +4493,22 @@ class MStoreTypedArrayElement
         return getOperand(1);
     }
     MDefinition *value() const {
         return getOperand(2);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
+    bool racy() const {
+        return racy_;
+    }
+    void setRacy() {
+        racy_ = true;
+    }
 };
 
 // Clamp input to range [0, 255] for Uint8ClampedArray.
 class MClampToUint8
   : public MUnaryInstruction,
     public ClampPolicy
 {
     MClampToUint8(MDefinition *input)
@@ -5049,16 +5219,37 @@ class MFunctionEnvironment
         return new MFunctionEnvironment(function);
     }
 
     MDefinition *function() const {
         return getOperand(0);
     }
 };
 
+// Loads the current js::ForkJoinSlice*.
+// Only applicable in ParallelExecution.
+class MParSlice
+  : public MNullaryInstruction
+{
+  public:
+    MParSlice()
+        : MNullaryInstruction()
+    {
+        setResultType(MIRType_ForkJoinSlice);
+    }
+
+    INSTRUCTION_HEADER(ParSlice);
+
+    AliasSet getAliasSet() const {
+        // Indicate that this instruction reads nothing, stores nothing.
+        // (For all intents and purposes)
+        return AliasSet::None();
+    }
+};
+
 // Store to vp[slot] (slots that are not inline in an object).
 class MStoreSlot
   : public MBinaryInstruction,
     public SingleObjectPolicy
 {
     uint32_t slot_;
     MIRType slotType_;
     bool needsBarrier_;
@@ -5890,16 +6081,72 @@ class MGetArgument
     bool congruentTo(MDefinition *const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
    }
 };
 
+class MParWriteGuard
+  : public MBinaryInstruction,
+    public ObjectPolicy<1>
+{
+    MParWriteGuard(MDefinition *parThreadContext,
+                   MDefinition *obj)
+      : MBinaryInstruction(parThreadContext, obj)
+    {
+        setResultType(MIRType_None);
+        setGuard();
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(ParWriteGuard);
+
+    static MParWriteGuard *New(MDefinition *parThreadContext, MDefinition *obj) {
+        return new MParWriteGuard(parThreadContext, obj);
+    }
+    MDefinition *parSlice() const {
+        return getOperand(0);
+    }
+    MDefinition *object() const {
+        return getOperand(1);
+    }
+    BailoutKind bailoutKind() const {
+        return Bailout_Normal;
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+};
+
+class MParDump
+  : public MUnaryInstruction,
+    public BoxPolicy<0>
+{
+  public:
+    INSTRUCTION_HEADER(ParDump);
+
+    MParDump(MDefinition *v)
+      : MUnaryInstruction(v)
+    {
+        setResultType(MIRType_None);
+    }
+
+    MDefinition *value() const {
+        return getOperand(0);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+};
+
+
 // Given a value, guard that the value is in a particular TypeSet, then returns
 // that value.
 class MTypeBarrier : public MUnaryInstruction
 {
     BailoutKind bailoutKind_;
     const types::StackTypeSet *typeSet_;
 
     MTypeBarrier(MDefinition *def, const types::StackTypeSet *types)
@@ -6040,24 +6287,69 @@ class MNewCallObject : public MUnaryInst
 
     static MNewCallObject *New(HandleObject templateObj, MDefinition *slots) {
         return new MNewCallObject(templateObj, slots);
     }
 
     MDefinition *slots() {
         return getOperand(0);
     }
-    JSObject *templateObj() {
+    JSObject *templateObject() {
         return templateObj_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
+class MParNewCallObject : public MBinaryInstruction
+{
+    CompilerRootObject templateObj_;
+
+    MParNewCallObject(MDefinition *parSlice,
+                      JSObject *templateObj, MDefinition *slots)
+        : MBinaryInstruction(parSlice, slots),
+          templateObj_(templateObj)
+    {
+        setResultType(MIRType_Object);
+    }
+
+  public:
+    INSTRUCTION_HEADER(ParNewCallObject);
+
+    static MParNewCallObject *New(MDefinition *parSlice,
+                                  JSObject *templateObj,
+                                  MDefinition *slots) {
+        return new MParNewCallObject(parSlice, templateObj, slots);
+    }
+
+    static MParNewCallObject *New(MDefinition *parSlice,
+                                  MNewCallObject *originalInstruction) {
+        return New(parSlice,
+                   originalInstruction->templateObject(),
+                   originalInstruction->slots());
+    }
+
+    MDefinition *parSlice() const {
+        return getOperand(0);
+    }
+
+    MDefinition *slots() const {
+        return getOperand(1);
+    }
+
+    JSObject *templateObj() const {
+        return templateObj_;
+    }
+
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+};
+
 class MNewStringObject :
   public MUnaryInstruction,
   public StringPolicy
 {
     CompilerRootObject templateObj_;
 
     MNewStringObject(MDefinition *input, HandleObject templateObj)
       : MUnaryInstruction(input),
@@ -6153,16 +6445,48 @@ class MEnclosingScope : public MLoadFixe
     }
 
     AliasSet getAliasSet() const {
         // ScopeObject reserved slots are immutable.
         return AliasSet::None();
     }
 };
 
+// Creates a dense array of the given length.
+//
+// Note: the template object should be an *empty* dense array!
+class MParNewDenseArray : public MBinaryInstruction
+{
+    CompilerRootObject templateObject_;
+
+  public:
+    INSTRUCTION_HEADER(ParNewDenseArray);
+
+    MParNewDenseArray(MDefinition *parSlice,
+                      MDefinition *length,
+                      JSObject *templateObject)
+      : MBinaryInstruction(parSlice, length),
+        templateObject_(templateObject)
+    {
+        setResultType(MIRType_Object);
+    }
+
+    MDefinition *parSlice() const {
+        return getOperand(0);
+    }
+
+    MDefinition *length() const {
+        return getOperand(1);
+    }
+
+    JSObject *templateObject() const {
+        return templateObject_;
+    }
+};
+
 // A resume point contains the information needed to reconstruct the interpreter
 // state from a position in the JIT. See the big comment near resumeAfter() in
 // IonBuilder.cpp.
 class MResumePoint : public MNode
 {
   public:
     enum Mode {
         ResumeAt,    // Resume until before the current instruction
--- a/js/src/ion/MIRGraph.cpp
+++ b/js/src/ion/MIRGraph.cpp
@@ -62,16 +62,47 @@ MIRGraph::insertBlockAfter(MBasicBlock *
 }
 
 void
 MIRGraph::unmarkBlocks() {
     for (MBasicBlockIterator i(blocks_.begin()); i != blocks_.end(); i++)
         i->unmark();
 }
 
+MDefinition *
+MIRGraph::parSlice() {
+    // Search the entry block to find a par slice instruction.  If we do not
+    // find one, add one after the Start instruction.
+    //
+    // Note: the original design used a field in MIRGraph to cache the
+    // parSlice rather than searching for it again.  However, this
+    // could become out of date due to DCE.  Given that we do not
+    // generally have to search very far to find the par slice
+    // instruction if it exists, and that we don't look for it that
+    // often, I opted to simply eliminate the cache and search anew
+    // each time, so that it is that much easier to keep the IR
+    // coherent. - nmatsakis
+
+    MBasicBlock *entry = entryBlock();
+    JS_ASSERT(entry->info().executionMode() == ParallelExecution);
+
+    MInstruction *start = NULL;
+    for (MInstructionIterator ins(entry->begin()); ins != entry->end(); ins++) {
+        if (ins->isParSlice())
+            return *ins;
+        else if (ins->isStart())
+            start = *ins;
+    }
+    JS_ASSERT(start);
+
+    MParSlice *parSlice = new MParSlice();
+    entry->insertAfter(start, parSlice);
+    return parSlice;
+}
+
 MBasicBlock *
 MBasicBlock::New(MIRGraph &graph, CompileInfo &info,
                  MBasicBlock *pred, jsbytecode *entryPc, Kind kind)
 {
     MBasicBlock *block = new MBasicBlock(graph, info, entryPc, kind);
     if (!block->init())
         return NULL;
 
@@ -122,16 +153,32 @@ MBasicBlock::NewPendingLoopHeader(MIRGra
 }
 
 MBasicBlock *
 MBasicBlock::NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred)
 {
     return MBasicBlock::New(graph, info, pred, pred->pc(), SPLIT_EDGE);
 }
 
+MBasicBlock *
+MBasicBlock::NewParBailout(MIRGraph &graph, CompileInfo &info,
+                           MBasicBlock *pred, jsbytecode *entryPc)
+{
+    MBasicBlock *block = MBasicBlock::New(graph, info, pred, entryPc, NORMAL);
+    if (!block)
+        return NULL;
+
+    MParBailout *bailout = new MParBailout();
+    if (!bailout)
+        return NULL;
+
+    block->end(bailout);
+    return block;
+}
+
 MBasicBlock::MBasicBlock(MIRGraph &graph, CompileInfo &info, jsbytecode *pc, Kind kind)
     : earlyAbort_(false),
     graph_(graph),
     info_(info),
     stackPosition_(info_.firstStackSlot()),
     lastIns_(NULL),
     pc_(pc),
     lir_(NULL),
@@ -725,24 +772,37 @@ MBasicBlock::numSuccessors() const
 
 MBasicBlock *
 MBasicBlock::getSuccessor(size_t index) const
 {
     JS_ASSERT(lastIns());
     return lastIns()->getSuccessor(index);
 }
 
+size_t
+MBasicBlock::getSuccessorIndex(MBasicBlock *block) const
+{
+    JS_ASSERT(lastIns());
+    for (size_t i = 0; i < numSuccessors(); i++) {
+        if (getSuccessor(i) == block)
+            return i;
+    }
+    JS_NOT_REACHED("Invalid successor");
+}
+
 void
 MBasicBlock::replaceSuccessor(size_t pos, MBasicBlock *split)
 {
     JS_ASSERT(lastIns());
-    lastIns()->replaceSuccessor(pos, split);
 
-    // Note, successors-with-phis is not yet set.
-    JS_ASSERT(!successorWithPhis_);
+    // Note, during split-critical-edges, successors-with-phis is not yet set.
+    // During PAA, this case is handled before we enter.
+    JS_ASSERT_IF(successorWithPhis_, successorWithPhis_ != getSuccessor(pos));
+
+    lastIns()->replaceSuccessor(pos, split);
 }
 
 void
 MBasicBlock::replacePredecessor(MBasicBlock *old, MBasicBlock *split)
 {
     for (size_t i = 0; i < numPredecessors(); i++) {
         if (getPredecessor(i) == old) {
             predecessors_[i] = split;
@@ -788,16 +848,17 @@ MBasicBlock::removePredecessor(MBasicBlo
                 getPredecessor(j)->setSuccessorWithPhis(this, j - 1);
         }
 
         // Remove from pred list.
         MBasicBlock **ptr = predecessors_.begin() + i;
         predecessors_.erase(ptr);
         return;
     }
+
     JS_NOT_REACHED("predecessor was not found");
 }
 
 void
 MBasicBlock::inheritPhis(MBasicBlock *header)
 {
     for (MPhiIterator iter = header->phisBegin(); iter != header->phisEnd(); iter++) {
         MPhi *phi = *iter;
--- a/js/src/ion/MIRGraph.h
+++ b/js/src/ion/MIRGraph.h
@@ -74,16 +74,18 @@ class MBasicBlock : public TempObject, p
     static MBasicBlock *NewPopN(MIRGraph &graph, CompileInfo &info,
                                 MBasicBlock *pred, jsbytecode *entryPc, Kind kind, uint32_t popn);
     static MBasicBlock *NewWithResumePoint(MIRGraph &graph, CompileInfo &info,
                                            MBasicBlock *pred, jsbytecode *entryPc,
                                            MResumePoint *resumePoint);
     static MBasicBlock *NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info,
                                              MBasicBlock *pred, jsbytecode *entryPc);
     static MBasicBlock *NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred);
+    static MBasicBlock *NewParBailout(MIRGraph &graph, CompileInfo &info,
+                                      MBasicBlock *pred, jsbytecode *entryPc);
 
     bool dominates(MBasicBlock *other);
 
     void setId(uint32_t id) {
         id_ = id;
     }
     void setEarlyAbort() {
         earlyAbort_ = true;
@@ -160,18 +162,21 @@ class MBasicBlock : public TempObject, p
     bool addPredecessor(MBasicBlock *pred);
     bool addPredecessorPopN(MBasicBlock *pred, uint32_t popped);
 
     // Stranger utilities used for inlining.
     bool addPredecessorWithoutPhis(MBasicBlock *pred);
     void inheritSlots(MBasicBlock *parent);
     bool initEntrySlots();
 
-    // Replaces an edge for a given block with a new block. This is used for
-    // critical edge splitting.
+    // Replaces an edge for a given block with a new block. This is
+    // used for critical edge splitting and also for inserting
+    // bailouts during ParallelArrayAnalysis.
+    //
+    // Note: If successorWithPhis is set, you must not be replacing it.
     void replacePredecessor(MBasicBlock *old, MBasicBlock *split);
     void replaceSuccessor(size_t pos, MBasicBlock *split);
 
     // Removes `pred` from the predecessor list.  `pred` should not be
     // the final predecessor. If this block defines phis, removes the
     // entry for `pred` and updates the indices of later entries.
     // This may introduce redundant phis if the new block has fewer
     // than two predecessors.
@@ -389,16 +394,17 @@ class MBasicBlock : public TempObject, p
         return positionInPhiSuccessor_;
     }
     void setSuccessorWithPhis(MBasicBlock *successor, uint32_t id) {
         successorWithPhis_ = successor;
         positionInPhiSuccessor_ = id;
     }
     size_t numSuccessors() const;
     MBasicBlock *getSuccessor(size_t index) const;
+    size_t getSuccessorIndex(MBasicBlock *) const;
 
     // Specifies the closest loop header dominating this block.
     void setLoopHeader(MBasicBlock *loop) {
         JS_ASSERT(loop->isLoopHeader());
         loopHeader_ = loop;
     }
     MBasicBlock *loopHeader() const {
         return loopHeader_;
@@ -603,16 +609,24 @@ class MIRGraph
         return scripts_.append(script);
     }
     size_t numScripts() const {
         return scripts_.length();
     }
     JSScript **scripts() {
         return scripts_.begin();
     }
+
+    // The ParSlice is an instance of ForkJoinSlice*, it carries
+    // "per-helper-thread" information.  So as not to modify the
+    // calling convention for parallel code, we obtain the current
+    // slice from thread-local storage.  This helper method will
+    // lazilly insert an MParSlice instruction in the entry block and
+    // return the definition.
+    MDefinition *parSlice();
 };
 
 class MDefinitionIterator
 {
 
   friend class MBasicBlock;
 
   private:
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -67,16 +67,17 @@ namespace ion {
     _(Unbox)                                                                \
     _(GuardObject)                                                          \
     _(GuardString)                                                          \
     _(ToDouble)                                                             \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(ToString)                                                             \
     _(NewSlots)                                                             \
+    _(NewParallelArray)                                                     \
     _(NewArray)                                                             \
     _(NewObject)                                                            \
     _(NewDeclEnvObject)                                                     \
     _(NewCallObject)                                                        \
     _(NewStringObject)                                                      \
     _(InitProp)                                                             \
     _(Start)                                                                \
     _(OsrEntry)                                                             \
@@ -140,24 +141,42 @@ namespace ion {
     _(Floor)                                                                \
     _(Round)                                                                \
     _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
     _(FunctionBoundary)                                                     \
     _(GetDOMProperty)                                                       \
-    _(SetDOMProperty)
+    _(SetDOMProperty)                                                       \
+    _(ParCheckOverRecursed)                                                 \
+    _(ParNewCallObject)                                                     \
+    _(ParNew)                                                               \
+    _(ParNewDenseArray)                                                     \
+    _(ParBailout)                                                           \
+    _(ParLambda)                                                            \
+    _(ParSlice)                                                             \
+    _(ParWriteGuard)                                                        \
+    _(ParDump)                                                              \
+    _(ParCheckInterrupt)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
  MIR_OPCODE_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
 
-class MInstructionVisitor
+class MInstructionVisitor // interface i.e. pure abstract class
+{
+  public:
+#define VISIT_INS(op) virtual bool visit##op(M##op *) = 0;
+    MIR_OPCODE_LIST(VISIT_INS)
+#undef VISIT_INS
+};
+
+class MInstructionVisitorWithDefaults : public MInstructionVisitor
 {
   public:
 #define VISIT_INS(op) virtual bool visit##op(M##op *) { JS_NOT_REACHED("NYI: " #op); return false; }
     MIR_OPCODE_LIST(VISIT_INS)
 #undef VISIT_INS
 };
 
 } // namespace ion
new file mode 100644
--- /dev/null
+++ b/js/src/ion/ParallelArrayAnalysis.cpp
@@ -0,0 +1,848 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=99:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+
+#include "Ion.h"
+#include "MIR.h"
+#include "MIRGraph.h"
+#include "ParallelArrayAnalysis.h"
+#include "IonSpewer.h"
+#include "UnreachableCodeElimination.h"
+#include "IonAnalysis.h"
+
+#include "vm/ParallelDo.h"
+
+#include "vm/Stack.h"
+
+namespace js {
+namespace ion {
+
+using parallel::Spew;
+using parallel::SpewMIR;
+using parallel::SpewCompile;
+
+#define SAFE_OP(op)                             \
+    virtual bool visit##op(M##op *prop) { return true; }
+
+#define CUSTOM_OP(op)                        \
+    virtual bool visit##op(M##op *prop);
+
+#define DROP_OP(op)                             \
+    virtual bool visit##op(M##op *ins) {        \
+        MBasicBlock *block = ins->block();      \
+        block->discard(ins);                    \
+        return true;                            \
+    }
+
+#define PERMIT(T) (1 << T)
+
+#define PERMIT_NUMERIC (PERMIT(MIRType_Int32) | PERMIT(MIRType_Double))
+
+#define SPECIALIZED_OP(op, flags)                                               \
+    virtual bool visit##op(M##op *ins) {                                        \
+        return visitSpecializedInstruction(ins, ins->specialization(), flags);  \
+    }
+
+#define UNSAFE_OP(op)                                               \
+    virtual bool visit##op(M##op *ins) {                            \
+        SpewMIR(ins, "Unsafe");                                     \
+        return markUnsafe();                                        \
+    }
+
+#define WRITE_GUARDED_OP(op, obj)                   \
+    virtual bool visit##op(M##op *prop) {           \
+        return insertWriteGuard(prop, prop->obj()); \
+    }
+
+#define MAYBE_WRITE_GUARDED_OP(op, obj)                                       \
+    virtual bool visit##op(M##op *prop) {                                     \
+        if (prop->racy())                                                     \
+            return true;                                                      \
+        return insertWriteGuard(prop, prop->obj());                           \
+    }
+
+class ParallelArrayVisitor : public MInstructionVisitor
+{
+    JSContext *cx_;
+    ParallelCompileContext &compileContext_;
+    MIRGraph &graph_;
+    bool unsafe_;
+    MDefinition *parSlice_;
+
+    bool insertWriteGuard(MInstruction *writeInstruction,
+                          MDefinition *valueBeingWritten);
+
+    bool replaceWithParNew(MInstruction *newInstruction,
+                           JSObject *templateObject);
+
+    bool replace(MInstruction *oldInstruction,
+                 MInstruction *replacementInstruction);
+
+    bool visitSpecializedInstruction(MInstruction *ins, MIRType spec, uint32_t flags);
+
+    // Intended for use in a visitXyz() instruction like "return
+    // markUnsafe()".  Sets the unsafe flag and returns true (since
+    // this does not indicate an unrecoverable compilation failure).
+    bool markUnsafe() {
+        JS_ASSERT(!unsafe_);
+        unsafe_ = true;
+        return true;
+    }
+
+  public:
+    AutoObjectVector callTargets;
+
+    ParallelArrayVisitor(JSContext *cx, ParallelCompileContext &compileContext,
+                         MIRGraph &graph)
+      : cx_(cx),
+        compileContext_(compileContext),
+        graph_(graph),
+        unsafe_(false),
+        parSlice_(NULL),
+        callTargets(cx)
+    { }
+
+    void clearUnsafe() { unsafe_ = false; }
+    bool unsafe() { return unsafe_; }
+    MDefinition *parSlice() {
+        if (!parSlice_)
+            parSlice_ = graph_.parSlice();
+        return parSlice_;
+    }
+
+    bool convertToBailout(MBasicBlock *block, MInstruction *ins);
+
+    // I am taking the policy of blacklisting everything that's not
+    // obviously safe for now.  We can loosen as we need.
+
+    SAFE_OP(Constant)
+    SAFE_OP(Parameter)
+    SAFE_OP(Callee)
+    SAFE_OP(TableSwitch)
+    SAFE_OP(Goto)
+    CUSTOM_OP(Test)
+    CUSTOM_OP(Compare)
+    SAFE_OP(Phi)
+    SAFE_OP(Beta)
+    UNSAFE_OP(OsrValue)
+    UNSAFE_OP(OsrScopeChain)
+    UNSAFE_OP(ReturnFromCtor)
+    CUSTOM_OP(CheckOverRecursed)
+    DROP_OP(RecompileCheck)
+    UNSAFE_OP(DefVar)
+    UNSAFE_OP(DefFun)
+    UNSAFE_OP(CreateThis)
+    UNSAFE_OP(CreateThisWithTemplate)
+    UNSAFE_OP(CreateThisWithProto)
+    SAFE_OP(PrepareCall)
+    SAFE_OP(PassArg)
+    CUSTOM_OP(Call)
+    UNSAFE_OP(ApplyArgs)
+    SAFE_OP(BitNot)
+    UNSAFE_OP(TypeOf)
+    SAFE_OP(ToId)
+    SAFE_OP(BitAnd)
+    SAFE_OP(BitOr)
+    SAFE_OP(BitXor)
+    SAFE_OP(Lsh)
+    SAFE_OP(Rsh)
+    SPECIALIZED_OP(Ursh, PERMIT_NUMERIC)
+    SPECIALIZED_OP(MinMax, PERMIT_NUMERIC)
+    SAFE_OP(Abs)
+    SAFE_OP(Sqrt)
+    SAFE_OP(MathFunction)
+    SPECIALIZED_OP(Add, PERMIT_NUMERIC)
+    SPECIALIZED_OP(Sub, PERMIT_NUMERIC)
+    SPECIALIZED_OP(Mul, PERMIT_NUMERIC)
+    SPECIALIZED_OP(Div, PERMIT_NUMERIC)
+    SPECIALIZED_OP(Mod, PERMIT_NUMERIC)
+    UNSAFE_OP(Concat)
+    UNSAFE_OP(CharCodeAt)
+    UNSAFE_OP(FromCharCode)
+    SAFE_OP(Return)
+    CUSTOM_OP(Throw)
+    SAFE_OP(Box)     // Boxing just creates a JSVal, doesn't alloc.
+    SAFE_OP(Unbox)
+    SAFE_OP(GuardObject)
+    SAFE_OP(ToDouble)
+    SAFE_OP(ToInt32)
+    SAFE_OP(TruncateToInt32)
+    UNSAFE_OP(ToString)
+    SAFE_OP(NewSlots)
+    CUSTOM_OP(NewArray)
+    CUSTOM_OP(NewObject)
+    CUSTOM_OP(NewCallObject)
+    CUSTOM_OP(NewParallelArray)
+    UNSAFE_OP(InitProp)
+    SAFE_OP(Start)
+    UNSAFE_OP(OsrEntry)
+    SAFE_OP(Nop)
+    UNSAFE_OP(RegExp)
+    CUSTOM_OP(Lambda)
+    UNSAFE_OP(ImplicitThis)
+    SAFE_OP(Slots)
+    SAFE_OP(Elements)
+    SAFE_OP(ConstantElements)
+    SAFE_OP(LoadSlot)
+    WRITE_GUARDED_OP(StoreSlot, slots)
+    SAFE_OP(FunctionEnvironment) // just a load of func env ptr
+    SAFE_OP(TypeBarrier) // causes a bailout if the type is not found: a-ok with us
+    SAFE_OP(MonitorTypes) // causes a bailout if the type is not found: a-ok with us
+    UNSAFE_OP(GetPropertyCache)
+    UNSAFE_OP(GetElementCache)
+    UNSAFE_OP(BindNameCache)
+    SAFE_OP(GuardShape)
+    SAFE_OP(GuardClass)
+    SAFE_OP(ArrayLength)
+    SAFE_OP(TypedArrayLength)
+    SAFE_OP(TypedArrayElements)
+    SAFE_OP(InitializedLength)
+    WRITE_GUARDED_OP(SetInitializedLength, elements)
+    SAFE_OP(Not)
+    SAFE_OP(BoundsCheck)
+    SAFE_OP(BoundsCheckLower)
+    SAFE_OP(LoadElement)
+    SAFE_OP(LoadElementHole)
+    MAYBE_WRITE_GUARDED_OP(StoreElement, elements)
+    WRITE_GUARDED_OP(StoreElementHole, elements)
+    UNSAFE_OP(ArrayPopShift)
+    UNSAFE_OP(ArrayPush)
+    SAFE_OP(LoadTypedArrayElement)
+    SAFE_OP(LoadTypedArrayElementHole)
+    MAYBE_WRITE_GUARDED_OP(StoreTypedArrayElement, elements)
+    UNSAFE_OP(ClampToUint8)
+    SAFE_OP(LoadFixedSlot)
+    WRITE_GUARDED_OP(StoreFixedSlot, object)
+    UNSAFE_OP(CallGetProperty)
+    UNSAFE_OP(GetNameCache)
+    SAFE_OP(CallGetIntrinsicValue) // Bails in parallel mode
+    UNSAFE_OP(CallsiteCloneCache)
+    UNSAFE_OP(CallGetElement)
+    UNSAFE_OP(CallSetElement)
+    UNSAFE_OP(CallSetProperty)
+    UNSAFE_OP(DeleteProperty)
+    UNSAFE_OP(SetPropertyCache)
+    UNSAFE_OP(IteratorStart)
+    UNSAFE_OP(IteratorNext)
+    UNSAFE_OP(IteratorMore)
+    UNSAFE_OP(IteratorEnd)
+    SAFE_OP(StringLength)
+    UNSAFE_OP(ArgumentsLength)
+    UNSAFE_OP(GetArgument)
+    SAFE_OP(Floor)
+    SAFE_OP(Round)
+    UNSAFE_OP(InstanceOf)
+    CUSTOM_OP(InterruptCheck)
+    SAFE_OP(ParSlice)
+    SAFE_OP(ParNew)
+    SAFE_OP(ParNewDenseArray)
+    SAFE_OP(ParNewCallObject)
+    SAFE_OP(ParLambda)
+    SAFE_OP(ParDump)
+    SAFE_OP(ParBailout)
+    UNSAFE_OP(ArrayConcat)
+    UNSAFE_OP(GetDOMProperty)
+    UNSAFE_OP(SetDOMProperty)
+    UNSAFE_OP(NewStringObject)
+    UNSAFE_OP(Random)
+    UNSAFE_OP(Pow)
+    UNSAFE_OP(PowHalf)
+    UNSAFE_OP(RegExpTest)
+    UNSAFE_OP(CallInstanceOf)
+    UNSAFE_OP(FunctionBoundary)
+    UNSAFE_OP(GuardString)
+    UNSAFE_OP(NewDeclEnvObject)
+    UNSAFE_OP(In)
+    UNSAFE_OP(InArray)
+    SAFE_OP(ParWriteGuard)
+    SAFE_OP(ParCheckInterrupt)
+    SAFE_OP(ParCheckOverRecursed)
+    SAFE_OP(PolyInlineDispatch)
+
+    // It looks like this could easily be made safe:
+    UNSAFE_OP(ConvertElementsToDoubles)
+};
+
+bool
+ParallelCompileContext::appendToWorklist(HandleFunction fun)
+{
+    JS_ASSERT(fun);
+
+    if (!fun->isInterpreted())
+        return true;
+
+    RootedScript script(cx_, fun->nonLazyScript());
+
+    // Skip if we're disabled.
+    if (!script->canParallelIonCompile()) {
+        Spew(SpewCompile, "Skipping %p:%s:%u, canParallelIonCompile() is false",
+             fun.get(), script->filename, script->lineno);
+        return true;
+    }
+
+    // Skip if we're compiling off thread.
+    if (script->parallelIon == ION_COMPILING_SCRIPT) {
+        Spew(SpewCompile, "Skipping %p:%s:%u, off-main-thread compilation in progress",
+             fun.get(), script->filename, script->lineno);
+        return true;
+    }
+
+    // Skip if the code is expected to result in a bailout.
+    if (script->parallelIon && script->parallelIon->bailoutExpected()) {
+        Spew(SpewCompile, "Skipping %p:%s:%u, bailout expected",
+             fun.get(), script->filename, script->lineno);
+        return true;
+    }
+
+    // Skip if we haven't warmed up to get some type info. We're betting
+    // that the parallel kernel will be non-branchy for the most part, so
+    // this threshold is usually very low (1).
+    if (script->getUseCount() < js_IonOptions.usesBeforeCompileParallel) {
+        Spew(SpewCompile, "Skipping %p:%s:%u, use count %u < %u",
+             fun.get(), script->filename, script->lineno,
+             script->getUseCount(), js_IonOptions.usesBeforeCompileParallel);
+        return true;
+    }
+
+    for (uint32_t i = 0; i < worklist_.length(); i++) {
+        if (worklist_[i]->toFunction() == fun)
+            return true;
+    }
+
+    // Note that we add all possibly compilable functions to the worklist,
+    // even if they're already compiled. This is so that we can return
+    // Method_Compiled and not Method_Skipped if we have a worklist full of
+    // already-compiled functions.
+    return worklist_.append(fun);
+}
+
+bool
+ParallelCompileContext::analyzeAndGrowWorklist(MIRGenerator *mir, MIRGraph &graph)
+{
+    // Walk the basic blocks in a DFS.  When we encounter a block with an
+    // unsafe instruction, then we know that this block will bailout when
+    // executed.  Therefore, we replace the block.
+    //
+    // We don't need a worklist, though, because the graph is sorted
+    // in RPO.  Therefore, we just use the marked flags to tell us
+    // when we visited some predecessor of the current block.
+    ParallelArrayVisitor visitor(cx_, *this, graph);
+    graph.entryBlock()->mark();  // Note: in par. exec., we never enter from OSR.
+    uint32_t marked = 0;
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
+        if (mir->shouldCancel("ParallelArrayAnalysis"))
+            return false;
+
+        if (block->isMarked()) {
+            // Iterate through and transform the instructions.  Stop
+            // if we encounter an inherently unsafe operation, in
+            // which case we will transform this block into a bailout
+            // block.
+            MInstruction *instr = NULL;
+            for (MInstructionIterator ins(block->begin());
+                 ins != block->end() && !visitor.unsafe();)
+            {
+                if (mir->shouldCancel("ParallelArrayAnalysis"))
+                    return false;
+
+                // We may be removing or replacing the current
+                // instruction, so advance `ins` now.  Remember the
+                // last instr. we looked at for use later if it should
+                // prove unsafe.
+                instr = *ins++;
+
+                if (!instr->accept(&visitor))
+                    return false;
+            }
+
+            if (!visitor.unsafe()) {
+                // Count the number of reachable blocks.
+                marked++;
+
+                // Block consists of only safe instructions.  Visit its successors.
+                for (uint32_t i = 0; i < block->numSuccessors(); i++)
+                    block->getSuccessor(i)->mark();
+            } else {
+                // Block contains an unsafe instruction.  That means that once
+                // we enter this block, we are guaranteed to bailout.
+
+                // If this is the entry block, then there is no point
+                // in even trying to execute this function as it will
+                // always bailout.
+                if (*block == graph.entryBlock()) {
+                    Spew(SpewCompile, "Entry block contains unsafe MIR");
+                    return false;
+                }
+
+                // Otherwise, create a replacement that will.
+                if (!visitor.convertToBailout(*block, instr))
+                    return false;
+
+                JS_ASSERT(!block->isMarked());
+            }
+        }
+    }
+
+    // Append newly discovered outgoing callgraph edges to the worklist.
+    RootedFunction target(cx_);
+    for (uint32_t i = 0; i < visitor.callTargets.length(); i++) {
+        target = visitor.callTargets[i]->toFunction();
+        appendToWorklist(target);
+    }
+
+    Spew(SpewCompile, "Safe");
+    IonSpewPass("ParallelArrayAnalysis");
+
+    UnreachableCodeElimination uce(mir, graph);
+    if (!uce.removeUnmarkedBlocks(marked))
+        return false;
+    IonSpewPass("UCEAfterParallelArrayAnalysis");
+    AssertExtendedGraphCoherency(graph);
+
+    if (!removeResumePointOperands(mir, graph))
+        return false;
+    IonSpewPass("RemoveResumePointOperands");
+    AssertExtendedGraphCoherency(graph);
+
+    if (!EliminateDeadCode(mir, graph))
+        return false;
+    IonSpewPass("DCEAfterParallelArrayAnalysis");
+    AssertExtendedGraphCoherency(graph);
+
+    return true;
+}
+
+bool
+ParallelCompileContext::removeResumePointOperands(MIRGenerator *mir, MIRGraph &graph)
+{
+    // In parallel exec mode, nothing is effectful, therefore we do
+    // not need to reconstruct interpreter state and can simply
+    // bailout by returning a special code.  Ideally we'd either
+    // remove the unused resume points or else never generate them in
+    // the first place, but I encountered various assertions and
+    // crashes attempting to do that, so for the time being I simply
+    // replace their operands with undefined.  This prevents them from
+    // interfering with DCE and other optimizations.  It is also *necessary*
+    // to handle cases like this:
+    //
+    //     foo(a, b, c.bar())
+    //
+    // where `foo` was deemed to be an unsafe function to call.  This
+    // is because without neutering the ResumePoints, they would still
+    // refer to the MPassArg nodes generated for the call to foo().
+    // But the call to foo() is dead and has been removed, leading to
+    // an inconsistent IR and assertions at codegen time.
+
+    MConstant *udef = NULL;
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
+        if (udef)
+            replaceOperandsOnResumePoint(block->entryResumePoint(), udef);
+
+        for (MInstructionIterator ins(block->begin()); ins != block->end(); ins++) {
+            if (ins->isStart()) {
+                JS_ASSERT(udef == NULL);
+                udef = MConstant::New(UndefinedValue());
+                block->insertAfter(*ins, udef);
+            } else if (udef) {
+                if (MResumePoint *resumePoint = ins->resumePoint())
+                    replaceOperandsOnResumePoint(resumePoint, udef);
+            }
+        }
+    }
+    return true;
+}
+
+void
+ParallelCompileContext::replaceOperandsOnResumePoint(MResumePoint *resumePoint,
+                                                     MDefinition *withDef)
+{
+    for (size_t i = 0; i < resumePoint->numOperands(); i++)
+        resumePoint->replaceOperand(i, withDef);
+}
+
+bool
+ParallelArrayVisitor::visitTest(MTest *)
+{
+    return true;
+}
+
+bool
+ParallelArrayVisitor::visitCompare(MCompare *compare)
+{
+    MCompare::CompareType type = compare->compareType();
+    return type == MCompare::Compare_Int32 ||
+           type == MCompare::Compare_Double ||
+           type == MCompare::Compare_String;
+}
+
+bool
+ParallelArrayVisitor::convertToBailout(MBasicBlock *block, MInstruction *ins)
+{
+    JS_ASSERT(unsafe()); // `block` must have contained unsafe items
+    JS_ASSERT(block->isMarked()); // `block` must have been reachable to get here
+
+    // Clear the unsafe flag for subsequent blocks.
+    clearUnsafe();
+
+    // This block is no longer reachable.
+    block->unmark();
+
+    // Determine the best PC to use for the bailouts we'll be creating.
+    jsbytecode *pc = block->pc();
+    if (!pc)
+        pc = block->pc();
+
+    // Create a bailout block for each predecessor.  In principle, we
+    // only need one bailout block--in fact, only one per graph! But I
+    // found this approach easier to implement given the design of the
+    // MIR Graph construction routines.  Besides, most often `block`
+    // has only one predecessor.  Also, using multiple blocks helps to
+    // keep the PC information more accurate (though replacing `block`
+    // with exactly one bailout would be just as good).
+    for (size_t i = 0; i < block->numPredecessors(); i++) {
+        MBasicBlock *pred = block->getPredecessor(i);
+
+        // We only care about incoming edges from reachable predecessors.
+        if (!pred->isMarked())
+            continue;
+
+        // create bailout block to insert on this edge
+        MBasicBlock *bailBlock = MBasicBlock::NewParBailout(graph_, block->info(), pred, pc);
+        if (!bailBlock)
+            return false;
+
+        // if `block` had phis, we are replacing it with `bailBlock` which does not
+        if (pred->successorWithPhis() == block)
+            pred->setSuccessorWithPhis(NULL, 0);
+
+        // redirect the predecessor to the bailout block
+        uint32_t succIdx = pred->getSuccessorIndex(block);
+        pred->replaceSuccessor(succIdx, bailBlock);
+
+        // Insert the bailout block after `block` in the execution
+        // order.  This should satisfy the RPO requirements and
+        // moreover ensures that we will visit this block in our outer
+        // walk, thus allowing us to keep the count of marked blocks
+        // accurate.
+        graph_.insertBlockAfter(block, bailBlock);
+        bailBlock->mark();
+    }
+
+    return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Memory allocation
+//
+// Simple memory allocation opcodes---those which ultimately compile
+// down to a (possibly inlined) invocation of NewGCThing()---are
+// replaced with MParNew, which is supplied with the thread context.
+// These allocations will take place using per-helper-thread arenas.
+
+bool
+ParallelArrayVisitor::visitNewParallelArray(MNewParallelArray *ins)
+{
+    MParNew *parNew = new MParNew(parSlice(), ins->templateObject());
+    replace(ins, parNew);
+    return true;
+}
+
+bool
+ParallelArrayVisitor::visitNewCallObject(MNewCallObject *ins)
+{
+    // fast path: replace with ParNewCallObject op
+    MParNewCallObject *parNewCallObjectInstruction =
+        MParNewCallObject::New(parSlice(), ins);
+    replace(ins, parNewCallObjectInstruction);
+    return true;
+}
+
+bool
+ParallelArrayVisitor::visitLambda(MLambda *ins)
+{
+    if (ins->fun()->hasSingletonType() ||
+        types::UseNewTypeForClone(ins->fun()))
+    {
+        // slow path: bail on parallel execution.
+        return markUnsafe();
+    }
+
+    // fast path: replace with ParLambda op
+    MParLambda *parLambdaInstruction = MParLambda::New(parSlice(), ins);
+    replace(ins, parLambdaInstruction);
+    return true;
+}
+
+bool
+ParallelArrayVisitor::visitNewObject(MNewObject *newInstruction)
+{
+    if (newInstruction->shouldUseVM()) {
+        SpewMIR(newInstruction, "should use VM");
+        return markUnsafe();
+    }
+
+    return replaceWithParNew(newInstruction,
+                             newInstruction->templateObject());
+}
+
+bool
+ParallelArrayVisitor::visitNewArray(MNewArray *newInstruction)
+{
+    if (newInstruction->shouldUseVM()) {
+        SpewMIR(newInstruction, "should use VM");
+        return markUnsafe();
+    }
+
+    return replaceWithParNew(newInstruction,
+                             newInstruction->templateObject());
+}
+
+bool
+ParallelArrayVisitor::replaceWithParNew(MInstruction *newInstruction,
+                                        JSObject *templateObject)
+{
+    MParNew *parNewInstruction = new MParNew(parSlice(), templateObject);
+    replace(newInstruction, parNewInstruction);
+    return true;
+}
+
+bool
+ParallelArrayVisitor::replace(MInstruction *oldInstruction,
+                              MInstruction *replacementInstruction)
+{
+    MBasicBlock *block = oldInstruction->block();
+    block->insertBefore(oldInstruction, replacementInstruction);
+    oldInstruction->replaceAllUsesWith(replacementInstruction);
+    block->discard(oldInstruction);
+    return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Write Guards
+//
+// We only want to permit writes to locally guarded objects.
+// Furthermore, we want to avoid PICs and other non-thread-safe things
+// (though perhaps we should support PICs at some point).  If we
+// cannot determine the origin of an object, we can insert a write
+// guard which will check whether the object was allocated from the
+// per-thread-arena or not.
+
+bool
+ParallelArrayVisitor::insertWriteGuard(MInstruction *writeInstruction,
+                                       MDefinition *valueBeingWritten)
+{
+    // Many of the write operations do not take the JS object
+    // but rather something derived from it, such as the elements.
+    // So we need to identify the JS object:
+    MDefinition *object;
+    switch (valueBeingWritten->type()) {
+      case MIRType_Object:
+        object = valueBeingWritten;
+        break;
+
+      case MIRType_Slots:
+        switch (valueBeingWritten->op()) {
+          case MDefinition::Op_Slots:
+            object = valueBeingWritten->toSlots()->object();
+            break;
+
+          case MDefinition::Op_NewSlots:
+            // Values produced by new slots will ALWAYS be
+            // thread-local.
+            return true;
+
+          default:
+            SpewMIR(writeInstruction, "cannot insert write guard for %s",
+                    valueBeingWritten->opName());
+            return markUnsafe();
+        }
+        break;
+
+      case MIRType_Elements:
+        switch (valueBeingWritten->op()) {
+          case MDefinition::Op_Elements:
+            object = valueBeingWritten->toElements()->object();
+            break;
+
+          case MDefinition::Op_TypedArrayElements:
+            object = valueBeingWritten->toTypedArrayElements()->object();
+            break;
+
+          default:
+            SpewMIR(writeInstruction, "cannot insert write guard for %s",
+                    valueBeingWritten->opName());
+            return markUnsafe();
+        }
+        break;
+
+      default:
+        SpewMIR(writeInstruction, "cannot insert write guard for MIR Type %d",
+                valueBeingWritten->type());
+        return markUnsafe();
+    }
+
+    if (object->isUnbox())
+        object = object->toUnbox()->input();
+
+    switch (object->op()) {
+      case MDefinition::Op_ParNew:
+        // MParNew will always be creating something thread-local, omit the guard
+        SpewMIR(writeInstruction, "write to ParNew prop does not require guard");
+        return true;
+      default:
+        break;
+    }
+
+    MBasicBlock *block = writeInstruction->block();
+    MParWriteGuard *writeGuard = MParWriteGuard::New(parSlice(), object);
+    block->insertBefore(writeInstruction, writeGuard);
+    writeGuard->adjustInputs(writeGuard);
+    return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Calls
+//
+// We only support calls to interpreted functions that that have already been
+// Ion compiled. If a function has no IonScript, we bail out. The compilation
+// is done during warmup of the parallel kernel, see js::RunScript.
+
+static bool
+GetPossibleCallees(JSContext *cx, HandleScript script, jsbytecode *pc,
+                   types::StackTypeSet *calleeTypes, AutoObjectVector &targets)
+{
+    JS_ASSERT(calleeTypes);
+
+    if (calleeTypes->baseFlags() != 0)
+        return true;
+
+    unsigned objCount = calleeTypes->getObjectCount();
+
+    if (objCount == 0)
+        return true;
+
+    RootedFunction fun(cx);
+    for (unsigned i = 0; i < objCount; i++) {
+        RawObject obj = calleeTypes->getSingleObject(i);
+        if (obj && obj->isFunction()) {
+            fun = obj->toFunction();
+        } else {
+            types::TypeObject *typeObj = calleeTypes->getTypeObject(i);
+            if (!typeObj)
+                continue;
+            fun = typeObj->interpretedFunction;
+            if (!fun)
+                continue;
+        }
+
+        if (fun->isCloneAtCallsite()) {
+            fun = CloneFunctionAtCallsite(cx, fun, script, pc);
+            if (!fun)
+                return false;
+        }
+
+        if (!targets.append(fun))
+            return false;
+    }
+
+    return true;
+}
+
+bool
+ParallelArrayVisitor::visitCall(MCall *ins)
+{
+    JS_ASSERT(ins->getSingleTarget() || ins->calleeTypes());
+
+    // DOM? Scary.
+    if (ins->isDOMFunction()) {
+        SpewMIR(ins, "call to dom function");
+        return markUnsafe();
+    }
+
+    RootedFunction target(cx_, ins->getSingleTarget());
+    if (target) {
+        // Native? Scary.
+        if (target->isNative()) {
+            SpewMIR(ins, "call to native function");
+            return markUnsafe();
+        }
+        return callTargets.append(target);
+    }</