Merge mozilla-central and inbound
authorEd Morley <emorley@mozilla.com>
Wed, 08 May 2013 15:05:07 +0100
changeset 142206 5da1839640f5497bc6cdaadce2faa4ce299daee9
parent 142185 e19d0885977cd5ef8e0feff53b7906730b6294c2 (current diff)
parent 142205 6cea585acd2110a923f9c2ce958ff1d15b3b0111 (diff)
child 142207 cd020c8432e6bb2b3eb58255db1ad8adc40f0d03
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central and inbound
js/src/vm/ParallelDo.cpp
js/src/vm/ParallelDo.h
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -434,23 +434,26 @@ nsAccUtils::TextLength(Accessible* aAcce
   return text.Length();
 }
 
 bool
 nsAccUtils::MustPrune(Accessible* aAccessible)
 { 
   roles::Role role = aAccessible->Role();
 
-  // We don't prune buttons any more however AT don't expect children inside of
-  // button in general, we allow menu buttons to have children to make them
-  // accessible.
-  return role == roles::MENUITEM || 
-    role == roles::COMBOBOX_OPTION ||
-    role == roles::OPTION ||
-    role == roles::ENTRY ||
-    role == roles::FLAT_EQUATION ||
-    role == roles::PASSWORD_TEXT ||
-    role == roles::TOGGLE_BUTTON ||
-    role == roles::GRAPHIC ||
-    role == roles::SLIDER ||
-    role == roles::PROGRESSBAR ||
-    role == roles::SEPARATOR;
+  // Don't prune the tree for certain roles if the tree is more complex than
+  // a single text leaf.
+  return
+    (role == roles::MENUITEM ||
+     role == roles::COMBOBOX_OPTION ||
+     role == roles::OPTION ||
+     role == roles::ENTRY ||
+     role == roles::FLAT_EQUATION ||
+     role == roles::PASSWORD_TEXT ||
+     role == roles::PUSHBUTTON ||
+     role == roles::TOGGLE_BUTTON ||
+     role == roles::GRAPHIC ||
+     role == roles::SLIDER ||
+     role == roles::PROGRESSBAR ||
+     role == roles::SEPARATOR) &&
+    aAccessible->ContentChildCount() == 1 &&
+    aAccessible->ContentChildAt(0)->IsTextLeaf();
 }
--- a/accessible/tests/mochitest/hittest/test_general.html
+++ b/accessible/tests/mochitest/hittest/test_general.html
@@ -44,17 +44,17 @@ if (!MAC) {
       testChildAtPoint(txt, -1, 1, null, null);
 
       // ::MustPrune case, point is outside of root accessible.
       testChildAtPoint(txt, -10000, 10000, null, null);
 
       // Not specific case, point is inside of btn accessible.
       var btn = getAccessible("btn");
       var btnText = btn.firstChild;
-      testChildAtPoint(btn, 1, 1, btnText, btnText);
+      testChildAtPoint(btn, 1, 1, btn, btn);
   
       // Not specific case, point is outside of btn accessible.
       testChildAtPoint(btn, -1, 1, null, null);
 
       // Out of flow accessible testing, do not return out of flow accessible
       // because it's not a child of the accessible even visually it is.
       var rectArea = getNode("area").getBoundingClientRect();
       var outOfFlow = getNode("outofflow");
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -686,10 +686,14 @@ pref("b2g.version", @MOZ_B2G_VERSION@);
 // Disable console buffering to save memory.
 pref("consoleservice.buffered", false);
 
 #ifdef MOZ_WIDGET_GONK
 // Performance testing suggests 2k is a better page size for SQLite.
 pref("toolkit.storage.pageSize", 2048);
 #endif
 
+// Enable captive portal detection.
+pref("captivedetect.canonicalURL", "http://detectportal.firefox.com/success.txt");
+pref("captivedetect.canonicalContent", "success\n");
+
 // The url of the manifest we use for ADU pings.
 pref("ping.manifestURL", "https://marketplace.firefox.com/packaged.webapp");
--- a/browser/metro/base/content/bindings/cssthrobber.xml
+++ b/browser/metro/base/content/bindings/cssthrobber.xml
@@ -7,17 +7,17 @@
 <bindings
     xmlns="http://www.mozilla.org/xbl"
     xmlns:xbl="http://www.mozilla.org/xbl"
     xmlns:html="http://www.w3.org/1999/xhtml"
     xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <binding id="cssthrobberBinding" extends="xul:box">
     <content>
       <html:div class="progressContainer">
-        <html:div class="progressBall progressBall" />
-        <html:div class="progressBall progressBall" />
-        <html:div class="progressBall progressBall" />
-        <html:div class="progressBall progressBall" />
-        <html:div class="progressBall progressBall" />
+        <html:div class="progressBall" />
+        <html:div class="progressBall" />
+        <html:div class="progressBall" />
+        <html:div class="progressBall" />
+        <html:div class="progressBall" />
       </html:div>
     </content>
   </binding>
 </bindings>
--- a/build/macosx/mozconfig.common
+++ b/build/macosx/mozconfig.common
@@ -20,11 +20,8 @@ if [ -z "$CC" ]; then
 fi
 
 # If not set use the system default clang++
 if [ -z "$CXX" ]; then
     export CXX=clang++
 fi
 
 ac_add_options --with-ccache
-
-# Enable ECMAScript Internationalization API
-ac_add_options --enable-intl-api
--- a/build/mozconfig.vs2010-common
+++ b/build/mozconfig.vs2010-common
@@ -1,11 +1,8 @@
-# Pymake needs Windows-style paths. Use cmd.exe to hack around this.
-mk_export_correct_style() {
-  if test -n "${_PYMAKE}"; then
-    mk_add_options "export $1=$(cmd.exe //c echo %$1%)"
-  else
-    mk_add_options "export $1=$(eval echo \$$1)"
-  fi
-}
-
-# Enable ECMAScript Internationalization API
-ac_add_options --enable-intl-api
+# Pymake needs Windows-style paths. Use cmd.exe to hack around this.
+mk_export_correct_style() {
+  if test -n "${_PYMAKE}"; then
+    mk_add_options "export $1=$(cmd.exe //c echo %$1%)"
+  else
+    mk_add_options "export $1=$(eval echo \$$1)"
+  fi
+}
--- a/build/unix/mozconfig.linux
+++ b/build/unix/mozconfig.linux
@@ -1,7 +1,4 @@
 . "$topsrcdir/build/mozconfig.common"
 
 CC=/tools/gcc-4.5-0moz3/bin/gcc
 CXX=/tools/gcc-4.5-0moz3/bin/g++
-
-# Enable ECMAScript Internationalization API
-ac_add_options --enable-intl-api
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -671,16 +671,18 @@ MediaStreamGraphImpl::NotifyHasCurrentDa
     aStream->mNotifiedHasCurrentData = true;
   }
 }
 
 void
 MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTime,
                                                   MediaStream* aStream)
 {
+  MOZ_ASSERT(mRealtime, "Should only attempt to create audio streams in real-time mode");
+
   nsAutoTArray<bool,2> audioOutputStreamsFound;
   for (uint32_t i = 0; i < aStream->mAudioOutputStreams.Length(); ++i) {
     audioOutputStreamsFound.AppendElement(false);
   }
 
   if (!aStream->mAudioOutputs.IsEmpty()) {
     for (StreamBuffer::TrackIter tracks(aStream->GetStreamBuffer(), MediaSegment::AUDIO);
          !tracks.IsEnded(); tracks.Next()) {
@@ -727,16 +729,18 @@ MediaStreamGraphImpl::CreateOrDestroyAud
     }
   }
 }
 
 void
 MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
                                 GraphTime aFrom, GraphTime aTo)
 {
+  MOZ_ASSERT(mRealtime, "Should only attempt to play audio in realtime mode");
+
   if (aStream->mAudioOutputStreams.IsEmpty()) {
     return;
   }
 
   // When we're playing multiple copies of this stream at the same time, they're
   // perfectly correlated so adding volumes is the right thing to do.
   float volume = 0.0f;
   for (uint32_t i = 0; i < aStream->mAudioOutputs.Length(); ++i) {
@@ -800,16 +804,18 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
       t = end;
     }
   }
 }
 
 void
 MediaStreamGraphImpl::PlayVideo(MediaStream* aStream)
 {
+  MOZ_ASSERT(mRealtime, "Should only attempt to play video in realtime mode");
+
   if (aStream->mVideoOutputs.IsEmpty())
     return;
 
   // Display the next frame a bit early. This is better than letting the current
   // frame be displayed for too long.
   GraphTime framePosition = mCurrentTime + MEDIA_GRAPH_TARGET_PERIOD_MS;
   NS_ASSERTION(framePosition >= aStream->mBufferStartTime, "frame position before buffer?");
   StreamTime frameBufferTime = GraphTimeToStreamTime(aStream, framePosition);
@@ -1005,71 +1011,76 @@ MediaStreamGraphImpl::RunThread()
             ps->ProduceOutput(prevComputedTime, mStateComputedTime);
             NS_ASSERTION(stream->mBuffer.GetEnd() >=
                          GraphTimeToStreamTime(stream, mStateComputedTime),
                        "Stream did not produce enough data");
           }
         }
       }
       NotifyHasCurrentData(stream);
-      CreateOrDestroyAudioStreams(prevComputedTime, stream);
-      PlayAudio(stream, prevComputedTime, mStateComputedTime);
-      audioStreamsActive += stream->mAudioOutputStreams.Length();
-      PlayVideo(stream);
+      if (mRealtime) {
+        // Only playback audio and video in real-time mode
+        CreateOrDestroyAudioStreams(prevComputedTime, stream);
+        PlayAudio(stream, prevComputedTime, mStateComputedTime);
+        audioStreamsActive += stream->mAudioOutputStreams.Length();
+        PlayVideo(stream);
+      }
       SourceMediaStream* is = stream->AsSourceStream();
       if (is) {
         UpdateBufferSufficiencyState(is);
       }
       GraphTime end;
       if (!stream->mBlocked.GetAt(mCurrentTime, &end) || end < GRAPH_TIME_MAX) {
         allBlockedForever = false;
       }
     }
     if (ensureNextIteration || !allBlockedForever || audioStreamsActive > 0) {
       EnsureNextIteration();
     }
 
     // Send updates to the main thread and wait for the next control loop
     // iteration.
     {
-      // Not using MonitorAutoLock since we need to unlock in a way
-      // that doesn't match lexical scopes.
       MonitorAutoLock lock(mMonitor);
       PrepareUpdatesToMainThreadState();
       if (mForceShutDown || (IsEmpty() && mMessageQueue.IsEmpty())) {
         // Enter shutdown mode. The stable-state handler will detect this
         // and complete shutdown. Destroy any streams immediately.
         LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p waiting for main thread cleanup", this));
         // Commit to shutting down this graph object.
         mLifecycleState = LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP;
         // No need to Destroy streams here. The main-thread owner of each
         // stream is responsible for calling Destroy them.
         return;
       }
 
-      PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
-      TimeStamp now = TimeStamp::Now();
-      if (mNeedAnotherIteration) {
-        int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
-          int64_t((now - mCurrentTimeStamp).ToMilliseconds());
-        // Make sure timeoutMS doesn't overflow 32 bits by waking up at
-        // least once a minute, if we need to wake up at all
-        timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000));
-        timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS));
-        LOG(PR_LOG_DEBUG, ("Waiting for next iteration; at %f, timeout=%f",
-                           (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
-        mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
-      } else {
-        mWaitState = WAITSTATE_WAITING_INDEFINITELY;
-      }
-      if (timeout > 0) {
-        mMonitor.Wait(timeout);
-        LOG(PR_LOG_DEBUG, ("Resuming after timeout; at %f, elapsed=%f",
-                           (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
-                           (TimeStamp::Now() - now).ToSeconds()));
+      // No need to wait in non-realtime mode, just churn through the input as soon
+      // as possible.
+      if (mRealtime) {
+        PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
+        TimeStamp now = TimeStamp::Now();
+        if (mNeedAnotherIteration) {
+          int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
+            int64_t((now - mCurrentTimeStamp).ToMilliseconds());
+          // Make sure timeoutMS doesn't overflow 32 bits by waking up at
+          // least once a minute, if we need to wake up at all
+          timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000));
+          timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS));
+          LOG(PR_LOG_DEBUG, ("Waiting for next iteration; at %f, timeout=%f",
+                             (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
+          mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
+        } else {
+          mWaitState = WAITSTATE_WAITING_INDEFINITELY;
+        }
+        if (timeout > 0) {
+          mMonitor.Wait(timeout);
+          LOG(PR_LOG_DEBUG, ("Resuming after timeout; at %f, elapsed=%f",
+                             (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
+                             (TimeStamp::Now() - now).ToSeconds()));
+        }
       }
       mWaitState = WAITSTATE_RUNNING;
       mNeedAnotherIteration = false;
       messageQueue.SwapElements(mMessageQueue);
     }
   }
 }
 
@@ -1934,29 +1945,30 @@ ProcessedMediaStream::DestroyImpl()
 }
 
 /**
  * We make the initial mCurrentTime nonzero so that zero times can have
  * special meaning if necessary.
  */
 static const int32_t INITIAL_CURRENT_TIME = 1;
 
-MediaStreamGraphImpl::MediaStreamGraphImpl()
+MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime)
   : mCurrentTime(INITIAL_CURRENT_TIME)
   , mStateComputedTime(INITIAL_CURRENT_TIME)
   , mProcessingGraphUpdateIndex(0)
   , mPortCount(0)
   , mMonitor("MediaStreamGraphImpl")
   , mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
   , mWaitState(WAITSTATE_RUNNING)
   , mNeedAnotherIteration(false)
   , mForceShutDown(false)
   , mPostedRunInStableStateEvent(false)
   , mDetectedNotRunning(false)
   , mPostedRunInStableState(false)
+  , mRealtime(aRealtime)
 {
 #ifdef PR_LOGGING
   if (!gMediaStreamGraphLog) {
     gMediaStreamGraphLog = PR_NewLogModule("MediaStreamGraph");
   }
 #endif
 
   mCurrentTimeStamp = mInitialTimeStamp = TimeStamp::Now();
@@ -1987,23 +1999,43 @@ MediaStreamGraph::GetInstance()
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
   if (!gGraph) {
     if (!gShutdownObserverRegistered) {
       gShutdownObserverRegistered = true;
       nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
     }
 
-    gGraph = new MediaStreamGraphImpl();
+    gGraph = new MediaStreamGraphImpl(true);
     LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
   }
 
   return gGraph;
 }
 
+MediaStreamGraph*
+MediaStreamGraph::CreateNonRealtimeInstance()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Main thread only");
+
+  MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false);
+  return graph;
+}
+
+void
+MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Main thread only");
+  MOZ_ASSERT(aGraph != gGraph, "Should not destroy the global graph here");
+
+  MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
+  graph->ForceShutDown();
+  delete graph;
+}
+
 SourceMediaStream*
 MediaStreamGraph::CreateSourceStream(DOMMediaStream* aWrapper)
 {
   SourceMediaStream* stream = new SourceMediaStream(aWrapper);
   NS_ADDREF(stream);
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   stream->SetGraphImpl(graph);
   graph->AppendMessage(new CreateMessage(stream));
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -882,27 +882,31 @@ protected:
   bool mInCycle;
 };
 
 // Returns ideal audio rate for processing
 inline TrackRate IdealAudioRate() { return 48000; }
 
 /**
  * Initially, at least, we will have a singleton MediaStreamGraph per
- * process.
+ * process.  Each OfflineAudioContext object creates its own MediaStreamGraph
+ * object too.
  */
 class MediaStreamGraph {
 public:
   // We ensure that the graph current time advances in multiples of
   // IdealAudioBlockSize()/IdealAudioRate(). A stream that never blocks
   // and has a track with the ideal audio rate will produce audio in
   // multiples of the block size.
 
   // Main thread only
   static MediaStreamGraph* GetInstance();
+  static MediaStreamGraph* CreateNonRealtimeInstance();
+  static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);
+
   // Control API.
   /**
    * Create a stream that a media decoder (or some other source of
    * media data, such as a camera) can write to.
    */
   SourceMediaStream* CreateSourceStream(DOMMediaStream* aWrapper);
   /**
    * Create a stream that will form the union of the tracks of its input
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -98,21 +98,29 @@ protected:
   MediaStream* mStream;
 };
 
 /**
  * The implementation of a media stream graph. This class is private to this
  * file. It's not in the anonymous namespace because MediaStream needs to
  * be able to friend it.
  *
- * Currently we only have one per process.
+ * Currently we have one global instance per process, and one per each
+ * OfflineAudioContext object.
  */
 class MediaStreamGraphImpl : public MediaStreamGraph {
 public:
-  MediaStreamGraphImpl();
+  /**
+   * Set aRealtime to true in order to create a MediaStreamGraph which provides
+   * support for real-time audio and video.  Set it to false in order to create
+   * a non-realtime instance which just churns through its inputs and produces
+   * output.  Those objects currently only support audio, and are used to
+   * implement OfflineAudioContext.  They do not support MediaStream inputs.
+   */
+  explicit MediaStreamGraphImpl(bool aRealtime);
   ~MediaStreamGraphImpl()
   {
     NS_ASSERTION(IsEmpty(),
                  "All streams should have been destroyed by messages from the main thread");
     LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p destroyed", this));
   }
 
   // Main thread only.
@@ -501,13 +509,18 @@ public:
    * LIFECYCLE_RUNNING, this can be relied on to not change unexpectedly.
    */
   bool mDetectedNotRunning;
   /**
    * True when a stable state runner has been posted to the appshell to run
    * RunInStableState at the next stable state.
    */
   bool mPostedRunInStableState;
+  /**
+   * True when processing real-time audio/video.  False when processing non-realtime
+   * audio.
+   */
+  bool mRealtime;
 };
 
 }
 
 #endif /* MEDIASTREAMGRAPHIMPL_H_ */
--- a/editor/txmgr/src/nsTransactionStack.cpp
+++ b/editor/txmgr/src/nsTransactionStack.cpp
@@ -7,94 +7,90 @@
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupportsUtils.h"
 #include "nsTransactionItem.h"
 #include "nsTransactionStack.h"
 #include "nscore.h"
 
 nsTransactionStack::nsTransactionStack(nsTransactionStack::Type aType)
-  : mQue(0)
-  , mType(aType)
+  : mType(aType)
 {
 }
 
 nsTransactionStack::~nsTransactionStack()
 {
   Clear();
 }
 
 void
 nsTransactionStack::Push(nsTransactionItem *aTransaction)
 {
   if (!aTransaction) {
     return;
   }
 
-  /* nsDeque's Push() method adds new items at the back
-   * of the deque.
-   */
-  NS_ADDREF(aTransaction);
-  mQue.Push(aTransaction);
+  // The stack's bottom is the front of the deque, and the top is the back.
+  mDeque.push_back(aTransaction);
 }
 
 already_AddRefed<nsTransactionItem>
 nsTransactionStack::Pop()
 {
-  /* nsDeque is a FIFO, so the top of our stack is actually
-   * the back of the deque.
-   */
-  return static_cast<nsTransactionItem*> (mQue.Pop());
+  if (mDeque.empty()) {
+    return nullptr;
+  }
+  nsRefPtr<nsTransactionItem> ret = mDeque.back().forget();
+  mDeque.pop_back();
+  return ret.forget();
 }
 
 already_AddRefed<nsTransactionItem>
 nsTransactionStack::PopBottom()
 {
-  /* nsDeque is a FIFO, so the bottom of our stack is actually
-   * the front of the deque.
-   */
-  return static_cast<nsTransactionItem*> (mQue.PopFront());
+  if (mDeque.empty()) {
+    return nullptr;
+  }
+  nsRefPtr<nsTransactionItem> ret = mDeque.front().forget();
+  mDeque.pop_front();
+  return ret.forget();
 }
 
 already_AddRefed<nsTransactionItem>
 nsTransactionStack::Peek()
 {
-  nsRefPtr<nsTransactionItem> transaction;
-  if (mQue.GetSize()) {
-    transaction = static_cast<nsTransactionItem*>(mQue.Last());
+  if (mDeque.empty()) {
+    return nullptr;
   }
-
-  return transaction.forget();
+  nsRefPtr<nsTransactionItem> ret = mDeque.back();
+  return ret.forget();
 }
 
 already_AddRefed<nsTransactionItem>
 nsTransactionStack::GetItem(int32_t aIndex)
 {
-  nsRefPtr<nsTransactionItem> transaction;
-  if (aIndex >= 0 && aIndex < mQue.GetSize()) {
-    transaction = static_cast<nsTransactionItem*>(mQue.ObjectAt(aIndex));
+  if (aIndex < 0 || aIndex >= static_cast<int32_t>(mDeque.size())) {
+    return nullptr;
   }
-
-  return transaction.forget();
+  nsRefPtr<nsTransactionItem> ret = mDeque[aIndex];
+  return ret.forget();
 }
 
 void
 nsTransactionStack::Clear()
 {
-  nsRefPtr<nsTransactionItem> tx;
-
-  do {
-    tx = mType == FOR_UNDO ? Pop() : PopBottom();
-  } while (tx);
+  while (!mDeque.empty()) {
+    nsRefPtr<nsTransactionItem> tx = mType == FOR_UNDO ? Pop() : PopBottom();
+  };
 }
 
 void
 nsTransactionStack::DoTraverse(nsCycleCollectionTraversalCallback &cb)
 {
-  for (int32_t i = 0, qcount = mQue.GetSize(); i < qcount; ++i) {
-    nsTransactionItem *item =
-      static_cast<nsTransactionItem*>(mQue.ObjectAt(i));
+  int32_t size = mDeque.size();
+  for (int32_t i = 0; i < size; ++i) {
+    nsTransactionItem* item = mDeque[i];
     if (item) {
-      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "transaction stack mQue[i]");
+      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "transaction stack mDeque[i]");
       cb.NoteNativeChild(item, NS_CYCLE_COLLECTION_PARTICIPANT(nsTransactionItem));
     }
   }
 }
--- a/editor/txmgr/src/nsTransactionStack.h
+++ b/editor/txmgr/src/nsTransactionStack.h
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsTransactionStack_h__
 #define nsTransactionStack_h__
 
-#include "nsCOMPtr.h"
-#include "nsDeque.h"
+#include <deque>
+#include "nsAutoPtr.h"
 
 class nsCycleCollectionTraversalCallback;
 class nsTransactionItem;
 
 class nsTransactionStack
 {
 public:
   enum Type { FOR_UNDO, FOR_REDO };
@@ -21,19 +21,19 @@ public:
   ~nsTransactionStack();
 
   void Push(nsTransactionItem *aTransactionItem);
   already_AddRefed<nsTransactionItem> Pop();
   already_AddRefed<nsTransactionItem> PopBottom();
   already_AddRefed<nsTransactionItem> Peek();
   already_AddRefed<nsTransactionItem> GetItem(int32_t aIndex);
   void Clear();
-  int32_t GetSize() { return mQue.GetSize(); }
+  int32_t GetSize() { return mDeque.size(); }
 
   void DoUnlink() { Clear(); }
   void DoTraverse(nsCycleCollectionTraversalCallback &cb);
 
 private:
-  nsDeque mQue;
+  std::deque<nsRefPtr<nsTransactionItem> > mDeque;
   const Type mType;
 };
 
 #endif // nsTransactionStack_h__
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -107,17 +107,17 @@ public:
   {
   }
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
   virtual TextureSourceD3D11* AsSourceD3D11() MOZ_OVERRIDE { return this; }
 
   virtual ID3D11Texture2D *GetD3D11Texture() MOZ_OVERRIDE {
-    return mIsTiled ? mTileTextures[mCurrentTile] : TextureSourceD3D11::GetD3D11Texture();
+    return mIsTiled ? mTileTextures[mCurrentTile].get() : TextureSourceD3D11::GetD3D11Texture();
   }
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
   virtual LayerRenderState GetRenderState() { return LayerRenderState(); }
 
   virtual bool Lock() MOZ_OVERRIDE { return true; }
 
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -535,17 +535,17 @@ imgStatusTracker::CalculateAndApplyDiffe
   // Now that we've calculated the difference in state, synchronize our state
   // with the other tracker.
 
   // First, actually synchronize our state.
   mState |= diff.mDiffState | loadState;
   if (diff.mUnblockedOnload) {
     mState &= ~stateBlockingOnload;
   }
-  mImageStatus = other->mImageStatus;
+
   mIsMultipart = other->mIsMultipart;
   mHadLastPart = other->mHadLastPart;
   mImageStatus |= other->mImageStatus;
   mHasBeenDecoded = mHasBeenDecoded || other->mHasBeenDecoded;
 
   // The error state is sticky and overrides all other bits.
   if (mImageStatus & imgIRequest::STATUS_ERROR) {
     mImageStatus = imgIRequest::STATUS_ERROR;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -113,17 +113,16 @@ 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 \
--- a/js/src/builtin/ParallelArray.js
+++ b/js/src/builtin/ParallelArray.js
@@ -290,19 +290,19 @@ function ParallelArrayBuild(self, shape,
     if (ShouldForceSequential())
       break parallel;
     if (!TRY_PARALLEL(mode))
       break parallel;
     if (computefunc === fillN)
       break parallel;
 
     var chunks = ComputeNumChunks(length);
-    var numSlices = ParallelSlices();
+    var numSlices = ForkJoinSlices();
     var info = ComputeAllSliceBounds(chunks, numSlices);
-    ParallelDo(constructSlice, CheckParallel(mode));
+    ForkJoin(constructSlice, CheckParallel(mode));
     return;
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
   computefunc(0, length);
   return;
 
@@ -380,19 +380,19 @@ function ParallelArrayMap(func, mode) {
 
   parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc
     if (ShouldForceSequential())
       break parallel;
     if (!TRY_PARALLEL(mode))
       break parallel;
 
     var chunks = ComputeNumChunks(length);
-    var numSlices = ParallelSlices();
+    var numSlices = ForkJoinSlices();
     var info = ComputeAllSliceBounds(chunks, numSlices);
-    ParallelDo(mapSlice, CheckParallel(mode));
+    ForkJoin(mapSlice, CheckParallel(mode));
     return NewParallelArray(ParallelArrayView, [length], buffer, 0);
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
   for (var i = 0; i < length; i++) {
     // Note: Unlike JS arrays, parallel arrays cannot have holes.
     var v = func(self.get(i), i, self);
@@ -435,23 +435,23 @@ function ParallelArrayReduce(func, mode)
 
   parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc
     if (ShouldForceSequential())
       break parallel;
     if (!TRY_PARALLEL(mode))
       break parallel;
 
     var chunks = ComputeNumChunks(length);
-    var numSlices = ParallelSlices();
+    var numSlices = ForkJoinSlices();
     if (chunks < numSlices)
       break parallel;
 
     var info = ComputeAllSliceBounds(chunks, numSlices);
     var subreductions = NewDenseArray(numSlices);
-    ParallelDo(reduceSlice, CheckParallel(mode));
+    ForkJoin(reduceSlice, CheckParallel(mode));
     var accumulator = subreductions[0];
     for (var i = 1; i < numSlices; i++)
       accumulator = func(accumulator, subreductions[i]);
     return accumulator;
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
@@ -522,23 +522,23 @@ function ParallelArrayScan(func, mode) {
 
   parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc
     if (ShouldForceSequential())
       break parallel;
     if (!TRY_PARALLEL(mode))
       break parallel;
 
     var chunks = ComputeNumChunks(length);
-    var numSlices = ParallelSlices();
+    var numSlices = ForkJoinSlices();
     if (chunks < numSlices)
       break parallel;
     var info = ComputeAllSliceBounds(chunks, numSlices);
 
     // Scan slices individually (see comment on phase1()).
-    ParallelDo(phase1, CheckParallel(mode));
+    ForkJoin(phase1, CheckParallel(mode));
 
     // Compute intermediates array (see comment on phase2()).
     var intermediates = [];
     var accumulator = buffer[finalElement(0)];
     ARRAY_PUSH(intermediates, accumulator);
     for (var i = 1; i < numSlices - 1; i++) {
       accumulator = func(accumulator, buffer[finalElement(i)]);
       ARRAY_PUSH(intermediates, accumulator);
@@ -548,17 +548,17 @@ function ParallelArrayScan(func, mode) {
     // convert from chunks to indices (see comment on phase2()).
     for (var i = 0; i < numSlices; i++) {
       info[SLICE_POS(i)] = info[SLICE_START(i)] << CHUNK_SHIFT;
       info[SLICE_END(i)] = info[SLICE_END(i)] << CHUNK_SHIFT;
     }
     info[SLICE_END(numSlices - 1)] = std_Math_min(info[SLICE_END(numSlices - 1)], length);
 
     // Complete each slice using intermediates array (see comment on phase2()).
-    ParallelDo(phase2, CheckParallel(mode));
+    ForkJoin(phase2, CheckParallel(mode));
     return NewParallelArray(ParallelArrayView, [length], buffer, 0);
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
   scan(self.get(0), 0, length);
   return NewParallelArray(ParallelArrayView, [length], buffer, 0);
 
@@ -799,30 +799,30 @@ function ParallelArrayScatter(targets, d
       ThrowError(JSMSG_PAR_ARRAY_SCATTER_CONFLICT);
 
     return conflictFunc(elem1, elem2);
   }
 
 
   function parDivideOutputRange() {
     var chunks = ComputeNumChunks(targetsLength);
-    var numSlices = ParallelSlices();
+    var numSlices = ForkJoinSlices();
     var checkpoints = NewDenseArray(numSlices);
     for (var i = 0; i < numSlices; i++)
       UnsafeSetElement(checkpoints, i, 0);
 
     var buffer = NewDenseArray(length);
     var conflicts = NewDenseArray(length);
 
     for (var i = 0; i < length; i++) {
       UnsafeSetElement(buffer, i, defaultValue);
       UnsafeSetElement(conflicts, i, false);
     }
 
-    ParallelDo(fill, CheckParallel(mode));
+    ForkJoin(fill, CheckParallel(mode));
     return NewParallelArray(ParallelArrayView, [length], buffer, 0);
 
     function fill(sliceId, numSlices, warmup) {
       var indexPos = checkpoints[sliceId];
       var indexEnd = targetsLength;
       if (warmup)
         indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE);
 
@@ -844,17 +844,17 @@ function ParallelArrayScatter(targets, d
   }
 
   function parDivideScatterVector() {
     // Subtle: because we will be mutating the localBuffers and
     // conflict arrays in place, we can never replay an entry in the
     // target array for fear of inducing a conflict where none existed
     // before. Therefore, we must proceed not by chunks but rather by
     // individual indices.
-    var numSlices = ParallelSlices();
+    var numSlices = ForkJoinSlices();
     var info = ComputeAllSliceBounds(targetsLength, numSlices);
 
     // FIXME(bug 844890): Use typed arrays here.
     var localBuffers = NewDenseArray(numSlices);
     for (var i = 0; i < numSlices; i++)
       UnsafeSetElement(localBuffers, i, NewDenseArray(length));
     var localConflicts = NewDenseArray(numSlices);
     for (var i = 0; i < numSlices; i++) {
@@ -867,17 +867,17 @@ function ParallelArrayScatter(targets, d
     // Initialize the 0th buffer, which will become the output. For
     // the other buffers, we track which parts have been written to
     // using the conflict buffer so they do not need to be
     // initialized.
     var outputBuffer = localBuffers[0];
     for (var i = 0; i < length; i++)
       UnsafeSetElement(outputBuffer, i, defaultValue);
 
-    ParallelDo(fill, CheckParallel(mode));
+    ForkJoin(fill, CheckParallel(mode));
     mergeBuffers();
     return NewParallelArray(ParallelArrayView, [length], outputBuffer, 0);
 
     function fill(sliceId, numSlices, warmup) {
       var indexPos = info[SLICE_POS(sliceId)];
       var indexEnd = info[SLICE_END(sliceId)];
       if (warmup)
         indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE);
@@ -967,17 +967,17 @@ function ParallelArrayFilter(func, mode)
 
   parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc
     if (ShouldForceSequential())
       break parallel;
     if (!TRY_PARALLEL(mode))
       break parallel;
 
     var chunks = ComputeNumChunks(length);
-    var numSlices = ParallelSlices();
+    var numSlices = ForkJoinSlices();
     if (chunks < numSlices * 2)
       break parallel;
 
     var info = ComputeAllSliceBounds(chunks, numSlices);
 
     // Step 1. Compute which items from each slice of the result
     // buffer should be preserved. When we're done, we have an array
     // |survivors| containing a bitset for each chunk, indicating
@@ -985,25 +985,25 @@ function ParallelArrayFilter(func, mode)
     // |counts| containing the total number of items that are being
     // preserved from within one slice.
     //
     // FIXME(bug 844890): Use typed arrays here.
     var counts = NewDenseArray(numSlices);
     for (var i = 0; i < numSlices; i++)
       UnsafeSetElement(counts, i, 0);
     var survivors = NewDenseArray(chunks);
-    ParallelDo(findSurvivorsInSlice, CheckParallel(mode));
+    ForkJoin(findSurvivorsInSlice, CheckParallel(mode));
 
     // Step 2. Compress the slices into one contiguous set.
     var count = 0;
     for (var i = 0; i < numSlices; i++)
       count += counts[i];
     var buffer = NewDenseArray(count);
     if (count > 0)
-      ParallelDo(copySurvivorsInSlice, CheckParallel(mode));
+      ForkJoin(copySurvivorsInSlice, CheckParallel(mode));
 
     return NewParallelArray(ParallelArrayView, [count], buffer, 0);
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
   var buffer = [];
   for (var i = 0; i < length; i++) {
@@ -1248,17 +1248,17 @@ function ParallelArrayToString() {
  */
 function AssertSequentialIsOK(mode) {
   if (mode && mode.mode && mode.mode !== "seq" && ParallelTestsShouldPass())
     ThrowError(JSMSG_WRONG_VALUE, "parallel execution", "sequential was forced");
 }
 
 /**
  * Internal debugging tool: returns a function to be supplied to
- * ParallelDo() that will check that the parallel results
+ * ForkJoin() that will check that the parallel results
  * bailout/succeed as expected. Returns null if no mode is supplied
  * or we are building with some strange IF_DEF configuration such that
  * we don't expect parallel execution to work.
  */
 function CheckParallel(mode) {
   if (!mode || !ParallelTestsShouldPass())
     return null;
 
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -55,16 +55,17 @@ var ignoreClasses = {
 var ignoreCallees = {
     "js::Class.trace" : true,
     "js::Class.finalize" : true,
     "JSRuntime.destroyPrincipals" : true,
     "nsISupports.AddRef" : true,
     "nsISupports.Release" : true, // makes me a bit nervous; this is a bug but can happen
     "nsAXPCNativeCallContext.GetJSContext" : true,
     "js::ion::MDefinition.op" : true, // macro generated virtuals just return a constant
+    "js::ion::MDefinition.opName" : true, // macro generated virtuals just return a constant
     "js::ion::LInstruction.getDef" : true, // virtual but no implementation can GC
     "js::ion::IonCache.kind" : true, // macro generated virtuals just return a constant
 };
 
 function fieldCallCannotGC(csu, fullfield)
 {
     if (csu in ignoreClasses)
         return true;
--- a/js/src/devtools/rootAnalysis/run_complete
+++ b/js/src/devtools/rootAnalysis/run_complete
@@ -159,17 +159,17 @@ chdir $result_dir;
 my $status = run_build();
 
 # end of run commands.
 
 close(OUT);
 
 for my $pid (@waitpids) {
     waitpid($pid, 0);
-    $status ||= $s? >> 8;
+    $status ||= $? >> 8;
 }
 
 print "Exiting run_complete with status $status\n";
 exit $status;
 
 # get the IP address which a freshly created manager is listening on.
 sub get_manager_address
 {
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -3526,16 +3526,22 @@ TryAttachGetElemStub(JSContext *cx, Hand
         ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
         if (!typedArrayStub)
             return false;
 
         stub->addNewStub(typedArrayStub);
         return true;
     }
 
+    // GetElem operations on non-native objects other than typed arrays cannot
+    // be cached by either Baseline or Ion. Indicate this in the cache so that
+    // Ion does not generate a cache for this op.
+    if (!obj->isNative() && !obj->isTypedArray())
+        stub->noteNonNativeAccess();
+
     return true;
 }
 
 static bool
 DoGetElemFallback(JSContext *cx, BaselineFrame *frame, ICGetElem_Fallback *stub, HandleValue lhs,
                   HandleValue rhs, MutableHandleValue res)
 {
     RootedScript script(cx, frame->script());
@@ -5944,16 +5950,26 @@ ICGetProp_ArgumentsLength::Compiler::gen
     masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
     EmitReturnFromIC(masm);
 
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
+void
+BaselineScript::noteAccessedGetter(uint32_t pcOffset)
+{
+    ICEntry &entry = icEntryFromPCOffset(pcOffset);
+    ICFallbackStub *stub = entry.fallbackStub();
+
+    if (stub->isGetProp_Fallback())
+        stub->toGetProp_Fallback()->noteAccessedGetter();
+}
+
 //
 // SetProp_Fallback
 //
 
 // Attach an optimized stub for a SETPROP/SETGNAME/SETNAME op.
 static bool
 TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub,
                      HandleObject obj, HandleShape oldShape, uint32_t oldSlots,
--- a/js/src/ion/BaselineIC.h
+++ b/js/src/ion/BaselineIC.h
@@ -2748,16 +2748,23 @@ class ICGetElem_Fallback : public ICMoni
     static const uint32_t MAX_OPTIMIZED_STUBS = 16;
 
     static inline ICGetElem_Fallback *New(ICStubSpace *space, IonCode *code) {
         if (!code)
             return NULL;
         return space->allocate<ICGetElem_Fallback>(code);
     }
 
+    void noteNonNativeAccess() {
+        extra_ = 1;
+    }
+    bool hasNonNativeAccess() const {
+        return extra_;
+    }
+
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::GetElem_Fallback)
@@ -3678,23 +3685,32 @@ class ICGetProp_Fallback : public ICMoni
 
     static inline ICGetProp_Fallback *New(ICStubSpace *space, IonCode *code) {
         if (!code)
             return NULL;
         return space->allocate<ICGetProp_Fallback>(code);
     }
 
     static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
+    static const size_t ACCESSED_GETTER_BIT = 1;
+
     void noteUnoptimizableAccess() {
         extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
     }
     bool hadUnoptimizableAccess() const {
         return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT);
     }
 
+    void noteAccessedGetter() {
+        extra_ |= (1u << ACCESSED_GETTER_BIT);
+    }
+    bool hasAccessedGetter() const {
+        return extra_ & (1u << ACCESSED_GETTER_BIT);
+    }
+
     class Compiler : public ICStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::GetProp_Fallback)
         { }
--- a/js/src/ion/BaselineInspector.cpp
+++ b/js/src/ion/BaselineInspector.cpp
@@ -262,8 +262,35 @@ BaselineInspector::expectedBinaryArithSp
     if (dimorphicStub(pc, &stubs[0], &stubs[1])) {
         if (TryToSpecializeBinaryArithOp(stubs, 2, &result))
             return result;
     }
 
     return MIRType_None;
 }
 
+bool
+BaselineInspector::hasSeenNonNativeGetElement(jsbytecode *pc)
+{
+    if (!hasBaselineScript())
+        return false;
+
+    const ICEntry &entry = icEntryFromPC(pc);
+    ICStub *stub = entry.fallbackStub();
+
+    if (stub->isGetElem_Fallback())
+        return stub->toGetElem_Fallback()->hasNonNativeAccess();
+    return false;
+}
+
+bool
+BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc)
+{
+    if (!hasBaselineScript())
+        return false;
+
+    const ICEntry &entry = icEntryFromPC(pc);
+    ICStub *stub = entry.fallbackStub();
+
+    if (stub->isGetProp_Fallback())
+        return stub->toGetProp_Fallback()->hasAccessedGetter();
+    return false;
+}
--- a/js/src/ion/BaselineInspector.h
+++ b/js/src/ion/BaselineInspector.h
@@ -98,15 +98,18 @@ class BaselineInspector
 
     SetElemICInspector setElemICInspector(jsbytecode *pc) {
         return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback);
     }
 
     MIRType expectedResultType(jsbytecode *pc);
     MCompare::CompareType expectedCompareType(jsbytecode *pc);
     MIRType expectedBinaryArithSpecialization(jsbytecode *pc);
+
+    bool hasSeenNonNativeGetElement(jsbytecode *pc);
+    bool hasSeenAccessedGetter(jsbytecode *pc);
 };
 
 } // namespace ion
 } // namespace js
 
 #endif
 
--- a/js/src/ion/BaselineJIT.h
+++ b/js/src/ion/BaselineJIT.h
@@ -242,16 +242,18 @@ struct BaselineScript
 
     // Toggle debug traps (used for breakpoints and step mode) in the script.
     // If |pc| is NULL, toggle traps for all ops in the script. Else, only
     // toggle traps at |pc|.
     void toggleDebugTraps(JSScript *script, jsbytecode *pc);
 
     void toggleSPS(bool enable);
 
+    void noteAccessedGetter(uint32_t pcOffset);
+
     static size_t offsetOfFlags() {
         return offsetof(BaselineScript, flags_);
     }
 };
 
 inline bool
 IsBaselineEnabled(JSContext *cx)
 {
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -14,17 +14,16 @@
 #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"
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -6151,19 +6151,23 @@ IonBuilder::jsop_getelem()
 
     current->popn(2);
 
     MInstruction *ins;
 
     bool cacheable = obj->mightBeType(MIRType_Object) && !obj->mightBeType(MIRType_String) &&
         (index->mightBeType(MIRType_Int32) || index->mightBeType(MIRType_String));
 
+    bool nonNativeGetElement =
+        script()->analysis()->getCode(pc).nonNativeGetElement ||
+        inspector->hasSeenNonNativeGetElement(pc);
+
     // Turn off cacheing if the element is int32 and we've seen non-native objects as the target
     // of this getelem.
-    if (index->mightBeType(MIRType_Int32) && script()->analysis()->getCode(pc).nonNativeGetElement)
+    if (index->mightBeType(MIRType_Int32) && nonNativeGetElement)
         cacheable = false;
 
     types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
     bool barrier = PropertyReadNeedsTypeBarrier(cx, obj, NULL, types);
 
     // Always add a barrier if the index might be a string, so that the cache
     // can attach stubs for particular properties.
     if (index->mightBeType(MIRType_String))
@@ -7486,17 +7490,19 @@ IonBuilder::getPropTryInlineAccess(bool 
     return true;
 }
 
 bool
 IonBuilder::getPropTryCache(bool *emitted, HandlePropertyName name, HandleId id,
                             bool barrier, types::StackTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
-    bool accessGetter = script()->analysis()->getCode(pc).accessGetter;
+    bool accessGetter =
+        script()->analysis()->getCode(pc).accessGetter ||
+        inspector->hasSeenAccessedGetter(pc);
 
     MDefinition *obj = current->peek(-1);
 
     // The input value must either be an object, or we should have strong suspicions
     // that it can be safely unboxed to an object.
     if (obj->type() != MIRType_Object) {
         types::StackTypeSet *types = obj->resultTypeSet();
         if (!types || !types->objectOrSentinel())
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -462,17 +462,17 @@ class IonBuilder : public MIRGenerator
 
     // 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 inlineShouldForceSequentialOrInParallelSection(CallInfo &callInfo);
+    InliningStatus inlineForceSequentialOrInParallelSection(CallInfo &callInfo);
     InliningStatus inlineNewDenseArray(CallInfo &callInfo);
     InliningStatus inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo);
     InliningStatus inlineNewDenseArrayForParallelExecution(CallInfo &callInfo);
     InliningStatus inlineNewParallelArray(CallInfo &callInfo);
     InliningStatus inlineParallelArray(CallInfo &callInfo);
     InliningStatus inlineParallelArrayTail(CallInfo &callInfo,
                                            HandleFunction target,
                                            MDefinition *ctor,
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -20,18 +20,16 @@
 #include "IonMacroAssembler.h"
 #include "PcScriptCache.h"
 #include "PcScriptCache-inl.h"
 #include "gc/Marking.h"
 #include "SnapshotReader.h"
 #include "Safepoints.h"
 #include "VMFunctions.h"
 
-#include "vm/ParallelDo.h"
-
 namespace js {
 namespace ion {
 
 IonFrameIterator::IonFrameIterator(const IonActivationIterator &activations)
     : current_(activations.top()),
       type_(IonFrame_Exit),
       returnAddressToFp_(NULL),
       frameSize_(0),
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -654,17 +654,17 @@ LIRGenerator::visitTest(MTest *test)
             } else {
                 rhs = useRegister(right);
             }
             LCompareAndBranch *lir = new LCompareAndBranch(op, lhs, rhs, ifTrue, ifFalse);
             return add(lir, comp);
         }
 
         // Compare and branch doubles.
-        if (comp->compareType() == MCompare::Compare_Double) {
+        if (comp->isDoubleComparison()) {
             LAllocation lhs = useRegister(left);
             LAllocation rhs = useRegister(right);
             LCompareDAndBranch *lir = new LCompareDAndBranch(lhs, rhs, ifTrue, ifFalse);
             return add(lir, comp);
         }
 
         // Compare values.
         if (comp->compareType() == MCompare::Compare_Value) {
@@ -830,22 +830,18 @@ LIRGenerator::visitCompare(MCompare *com
             rhs = useAnyOrConstant(right);
         } else {
             rhs = useRegister(right);
         }
         return define(new LCompare(op, lhs, rhs), comp);
     }
 
     // Compare doubles.
-    if (comp->compareType() == MCompare::Compare_Double ||
-        comp->compareType() == MCompare::Compare_DoubleMaybeCoerceLHS ||
-        comp->compareType() == MCompare::Compare_DoubleMaybeCoerceRHS)
-    {
+    if (comp->isDoubleComparison())
         return define(new LCompareD(useRegister(left), useRegister(right)), comp);
-    }
 
     // Compare values.
     if (comp->compareType() == MCompare::Compare_Value) {
         LCompareV *lir = new LCompareV();
         if (!useBoxAtStart(lir, LCompareV::LhsInput, left))
             return false;
         if (!useBoxAtStart(lir, LCompareV::RhsInput, right))
             return false;
--- a/js/src/ion/MCallOptimize.cpp
+++ b/js/src/ion/MCallOptimize.cpp
@@ -100,19 +100,19 @@ IonBuilder::inlineNativeCall(CallInfo &c
         return inlineToObject(callInfo);
 #ifdef DEBUG
     if (native == intrinsic_Dump)
         return inlineDump(callInfo);
 #endif
 
     // Parallel intrinsics.
     if (native == intrinsic_ShouldForceSequential)
-        return inlineShouldForceSequentialOrInParallelSection(callInfo);
+        return inlineForceSequentialOrInParallelSection(callInfo);
     if (native == testingFunc_inParallelSection)
-        return inlineShouldForceSequentialOrInParallelSection(callInfo);
+        return inlineForceSequentialOrInParallelSection(callInfo);
     if (native == intrinsic_NewParallelArray)
         return inlineNewParallelArray(callInfo);
     if (native == ParallelArrayObject::construct)
         return inlineParallelArray(callInfo);
 
     return InliningStatus_NotInlined;
 }
 
@@ -1057,17 +1057,17 @@ IonBuilder::inlineUnsafeSetTypedArrayEle
 
     if (!resumeAfter(store))
         return false;
 
     return true;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineShouldForceSequentialOrInParallelSection(CallInfo &callInfo)
+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
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -1842,16 +1842,21 @@ class MCompare
     bool tryFold(bool *result);
     bool evaluateConstantOperands(bool *result);
     MDefinition *foldsTo(bool useValueNumbers);
 
     void infer(JSContext *cx, BaselineInspector *inspector, jsbytecode *pc);
     CompareType compareType() const {
         return compareType_;
     }
+    bool isDoubleComparison() const {
+        return compareType() == Compare_Double ||
+               compareType() == Compare_DoubleMaybeCoerceLHS ||
+               compareType() == Compare_DoubleMaybeCoerceRHS;
+    }
     void setCompareType(CompareType type) {
         compareType_ = type;
     }
     MIRType inputType();
 
     JSOp jsop() const {
         return jsop_;
     }
--- a/js/src/ion/ParallelArrayAnalysis.cpp
+++ b/js/src/ion/ParallelArrayAnalysis.cpp
@@ -10,17 +10,16 @@
 #include "MIR.h"
 #include "MIRGraph.h"
 #include "ParallelArrayAnalysis.h"
 #include "IonSpewer.h"
 #include "UnreachableCodeElimination.h"
 #include "IonAnalysis.h"
 
 #include "vm/Stack.h"
-#include "vm/ParallelDo.h"
 
 namespace js {
 namespace ion {
 
 using parallel::Spew;
 using parallel::SpewMIR;
 using parallel::SpewCompile;
 
--- a/js/src/ion/ParallelFunctions.cpp
+++ b/js/src/ion/ParallelFunctions.cpp
@@ -6,18 +6,16 @@
 
 #include "jsinterp.h"
 #include "ParallelFunctions.h"
 #include "IonSpewer.h"
 
 #include "jsinterpinlines.h"
 #include "jscompartmentinlines.h"
 
-#include "vm/ParallelDo.h"
-
 using namespace js;
 using namespace ion;
 
 using parallel::Spew;
 using parallel::SpewOps;
 using parallel::SpewBailouts;
 using parallel::SpewBailoutIR;
 
--- a/js/src/jsapi-tests/testXDR.cpp
+++ b/js/src/jsapi-tests/testXDR.cpp
@@ -33,28 +33,29 @@ CompileScriptForPrincipalsVersionOrigin(
            .setFileAndLine(filename, lineno)
            .setVersion(version);
     JSScript *script = JS::Compile(cx, obj, options, chars, nchars);
     free(chars);
     return script;
 }
 
 JSScript *
-FreezeThaw(JSContext *cx, JSScript *script)
+FreezeThaw(JSContext *cx, JS::HandleScript script)
 {
     // freeze
     uint32_t nbytes;
     void *memory = JS_EncodeScript(cx, script, &nbytes);
     if (!memory)
         return NULL;
 
     // thaw
-    script = JS_DecodeScript(cx, memory, nbytes, script->principals(), script->originPrincipals);
+    JSScript *script2 = JS_DecodeScript(cx, memory, nbytes,
+                                        script->principals(), script->originPrincipals);
     js_free(memory);
-    return script;
+    return script2;
 }
 
 static JSScript *
 GetScript(JSContext *cx, JS::HandleObject funobj)
 {
     return JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj));
 }
 
@@ -118,19 +119,19 @@ enum TestCase {
 
 JSScript *createScriptViaXDR(JSPrincipals *prin, JSPrincipals *orig, int testCase)
 {
     const char src[] =
         "function f() { return 1; }\n"
         "f;\n";
 
     JS::RootedObject global(cx, JS_GetGlobalObject(cx));
-    JSScript *script = CompileScriptForPrincipalsVersionOrigin(cx, global, prin, orig,
-                                                               src, strlen(src), "test", 1,
-                                                               JSVERSION_DEFAULT);
+    JS::RootedScript script(cx, CompileScriptForPrincipalsVersionOrigin(cx, global, prin, orig,
+                                                                        src, strlen(src), "test", 1,
+                                                                        JSVERSION_DEFAULT));
     if (!script)
         return NULL;
 
     if (testCase == TEST_SCRIPT || testCase == TEST_SERIALIZED_FUNCTION) {
         script = FreezeThaw(cx, script);
         if (!script)
             return NULL;
         if (testCase == TEST_SCRIPT)
@@ -158,17 +159,17 @@ BEGIN_TEST(testXDR_bug506491)
         "function makeClosure(s, name, value) {\n"
         "    eval(s);\n"
         "    Math.sin(value);\n"
         "    return let (n = name, v = value) function () { return String(v); };\n"
         "}\n"
         "var f = makeClosure('0;', 'status', 'ok');\n";
 
     // compile
-    JSScript *script = JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__);
+    JS::RootedScript script(cx, JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__));
     CHECK(script);
 
     script = FreezeThaw(cx, script);
     CHECK(script);
 
     // execute
     JS::RootedValue v2(cx);
     CHECK(JS_ExecuteScript(cx, global, script, v2.address()));
@@ -182,17 +183,17 @@ BEGIN_TEST(testXDR_bug506491)
     CHECK_SAME(v2, trueval);
     return true;
 }
 END_TEST(testXDR_bug506491)
 
 BEGIN_TEST(testXDR_bug516827)
 {
     // compile an empty script
-    JSScript *script = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__);
+    JS::RootedScript script(cx, JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__));
     CHECK(script);
 
     script = FreezeThaw(cx, script);
     CHECK(script);
 
     // execute with null result meaning no result wanted
     CHECK(JS_ExecuteScript(cx, global, script, NULL));
     return true;
@@ -203,17 +204,17 @@ BEGIN_TEST(testXDR_source)
 {
     const char *samples[] = {
         // This can't possibly fail to compress well, can it?
         "function f(x) { return x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x }",
         "short",
         NULL
     };
     for (const char **s = samples; *s; s++) {
-        JSScript *script = JS_CompileScript(cx, global, *s, strlen(*s), __FILE__, __LINE__);
+        JS::RootedScript script(cx, JS_CompileScript(cx, global, *s, strlen(*s), __FILE__, __LINE__));
         CHECK(script);
         script = FreezeThaw(cx, script);
         CHECK(script);
         JSString *out = JS_DecompileScript(cx, script, "testing", 0);
         CHECK(out);
         JSBool equal;
         CHECK(JS_StringEqualsAscii(cx, out, *s, &equal));
         CHECK(equal);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1904,36 +1904,39 @@ TriggerOperationCallback(JSRuntime *rt, 
     rt->gcTriggerReason = reason;
     rt->triggerOperationCallback();
 }
 
 void
 js::TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason)
 {
     /* Wait till end of parallel section to trigger GC. */
-    if (ForkJoinSlice *slice = ForkJoinSlice::Current()) {
-        slice->requestGC(reason);
+    if (InParallelSection()) {
+        ForkJoinSlice::Current()->requestGC(reason);
         return;
     }
 
     rt->assertValidThread();
 
     if (rt->isHeapBusy())
         return;
 
     JS::PrepareForFullGC(rt);
     TriggerOperationCallback(rt, reason);
 }
 
 void
 js::TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
 {
-    /* Wait till end of parallel section to trigger GC. */
-    if (ForkJoinSlice *slice = ForkJoinSlice::Current()) {
-        slice->requestZoneGC(zone, reason);
+    /*
+     * If parallel threads are running, wait till they
+     * are stopped to trigger GC.
+     */
+    if (InParallelSection()) {
+        ForkJoinSlice::Current()->requestZoneGC(zone, reason);
         return;
     }
 
     JSRuntime *rt = zone->rt;
     rt->assertValidThread();
 
     if (rt->isHeapBusy())
         return;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -33,16 +33,17 @@
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jsdbgapi.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
+#include "ion/BaselineJIT.h"
 #include "js/MemoryMetrics.h"
 #include "vm/Shape.h"
 
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
@@ -3763,16 +3764,30 @@ NativeGetInline(JSContext *cx,
     {
         jsbytecode *pc;
         JSScript *script = cx->stack.currentScript(&pc);
         if (script && script->hasAnalysis()) {
             analyze::Bytecode *code = script->analysis()->maybeCode(pc);
             if (code)
                 code->accessGetter = true;
         }
+
+#ifdef JS_ION
+        if (script && script->hasBaselineScript()) {
+            switch (JSOp(*pc)) {
+              case JSOP_GETPROP:
+              case JSOP_CALLPROP:
+              case JSOP_LENGTH:
+                script->baselineScript()->noteAccessedGetter(pc - script->code);
+                break;
+              default:
+                break;
+            }
+        }
+#endif
     }
 
     if (!allowGC)
         return false;
 
     if (!shape->get(cx,
                     MaybeRooted<JSObject*, allowGC>::toHandle(receiver),
                     MaybeRooted<JSObject*, allowGC>::toHandle(obj),
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -6,40 +6,196 @@
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "vm/ForkJoin.h"
 #include "vm/Monitor.h"
 #include "gc/Marking.h"
 
-#include "jsinferinlines.h"
+#ifdef JS_ION
+#  include "ion/ParallelArrayAnalysis.h"
+#endif
 
 #ifdef JS_THREADSAFE
 #  include "prthread.h"
+#  include "prprf.h"
 #endif
 
+#if defined(DEBUG) && defined(JS_THREADSAFE) && defined(JS_ION)
+#  include "ion/Ion.h"
+#  include "ion/MIR.h"
+#  include "ion/MIRGraph.h"
+#  include "ion/IonCompartment.h"
+#endif // DEBUG && THREADSAFE && ION
+
+// For extracting stack extent for each thread.
+#include "jsnativestack.h"
+
+#include "jsinferinlines.h"
+#include "jsinterpinlines.h"
+
 using namespace js;
+using namespace js::parallel;
+using namespace js::ion;
+
+///////////////////////////////////////////////////////////////////////////
+// Degenerate configurations
+//
+// When JS_THREADSAFE or JS_ION is not defined, we simply run the
+// |func| callback sequentially.  We also forego the feedback
+// altogether.
+
+static bool
+ExecuteSequentially(JSContext *cx_, HandleValue funVal);
+
+#if !defined(JS_THREADSAFE) || !defined(JS_ION)
+bool
+js::ForkJoin(JSContext *cx, CallArgs &args)
+{
+    RootedValue argZero(cx, args[0]);
+    return ExecuteSequentially(cx, argZero);
+}
+
+uint32_t
+js::ForkJoinSlices(JSContext *cx)
+{
+    return 1; // just the main thread
+}
+
+JSContext *
+ForkJoinSlice::acquireContext()
+{
+    return NULL;
+}
+
+void
+ForkJoinSlice::releaseContext()
+{
+}
+
+bool
+ForkJoinSlice::isMainThread()
+{
+    return true;
+}
+
+bool
+ForkJoinSlice::InitializeTLS()
+{
+    return true;
+}
+
+JSRuntime *
+ForkJoinSlice::runtime()
+{
+    JS_NOT_REACHED("Not THREADSAFE build");
+}
+
+bool
+ForkJoinSlice::check()
+{
+    JS_NOT_REACHED("Not THREADSAFE build");
+    return true;
+}
+
+void
+ForkJoinSlice::requestGC(JS::gcreason::Reason reason)
+{
+    JS_NOT_REACHED("Not THREADSAFE build");
+}
+
+void
+ForkJoinSlice::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
+{
+    JS_NOT_REACHED("Not THREADSAFE build");
+}
+
+#endif // !JS_THREADSAFE || !JS_ION
+
+///////////////////////////////////////////////////////////////////////////
+// All configurations
+//
+// Some code that is shared between degenerate and parallel configurations.
+
+static bool
+ExecuteSequentially(JSContext *cx, HandleValue funVal)
+{
+    uint32_t numSlices = ForkJoinSlices(cx);
+    FastInvokeGuard fig(cx, funVal);
+    for (uint32_t i = 0; i < numSlices; i++) {
+        InvokeArgsGuard &args = fig.args();
+        if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &args))
+            return false;
+        args.setCallee(funVal);
+        args.setThis(UndefinedValue());
+        args[0].setInt32(i);
+        args[1].setInt32(numSlices);
+        args[2].setBoolean(!!cx->runtime->parallelWarmup);
+        if (!fig.invoke(cx))
+            return false;
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Parallel configurations
+//
+// The remainder of this file is specific to cases where both
+// JS_THREADSAFE and JS_ION are enabled.
 
 #if defined(JS_THREADSAFE) && defined(JS_ION)
 
+///////////////////////////////////////////////////////////////////////////
+// Class Declarations and Function Prototypes
+
+namespace js {
+
 unsigned ForkJoinSlice::ThreadPrivateIndex;
 bool ForkJoinSlice::TLSInitialized;
 
-class js::ForkJoinShared : public TaskExecutor, public Monitor
+class ParallelDo
+{
+  public:
+    // For tests, make sure to keep this in sync with minItemsTestingThreshold.
+    const static uint32_t MAX_BAILOUTS = 3;
+    uint32_t bailouts;
+    AutoScriptVector pendingInvalidations_;
+
+    ParallelDo(JSContext *cx, HandleObject fun);
+    ExecutionStatus apply();
+
+  private:
+    JSContext *cx_;
+    HeapPtrObject fun_;
+
+    inline bool executeSequentially();
+
+    MethodStatus compileForParallelExecution();
+    ExecutionStatus disqualifyFromParallelExecution();
+    bool invalidateBailedOutScripts();
+    bool warmupForParallelExecution();
+    ParallelResult executeInParallel();
+    inline static bool hasScript(Vector<types::RecompileInfo> &scripts,
+                                 JSScript *script);
+    inline bool hasNoPendingInvalidations();
+}; // class ParallelDo
+
+class ForkJoinShared : public TaskExecutor, public Monitor
 {
     /////////////////////////////////////////////////////////////////////////
     // Constant fields
 
     JSContext *const cx_;          // Current context
     ThreadPool *const threadPool_; // The thread pool.
-    ForkJoinOp &op_;               // User-defined operations to be perf. in par.
+    HandleObject fun_;             // The JavaScript function to execute.
     const uint32_t numSlices_;     // Total number of threads.
     PRCondVar *rendezvousEnd_;     // Cond. var used to signal end of rendezvous.
     PRLock *cxLock_;               // Locks cx_ for parallel VM calls.
+    AutoScriptVector &pendingInvalidations_; // From ParallelDo
 
     /////////////////////////////////////////////////////////////////////////
     // Per-thread arenas
     //
     // Each worker thread gets an arena to use when allocating.
 
     Vector<Allocator *, 16> allocators_;
 
@@ -94,19 +250,20 @@ class js::ForkJoinShared : public TaskEx
 
     // Permits other threads to resume execution.  Must be invoked from the
     // main thread after a call to initiateRendezvous().
     void endRendezvous(ForkJoinSlice &threadCx);
 
   public:
     ForkJoinShared(JSContext *cx,
                    ThreadPool *threadPool,
-                   ForkJoinOp &op,
+                   HandleObject fun,
                    uint32_t numSlices,
-                   uint32_t uncompleted);
+                   uint32_t uncompleted,
+                   AutoScriptVector &pendingInvalidations);
     ~ForkJoinShared();
 
     bool init();
 
     ParallelResult execute();
 
     // Invoked from parallel worker threads:
     virtual void executeFromWorker(uint32_t threadId, uintptr_t stackLimit);
@@ -128,71 +285,390 @@ class js::ForkJoinShared : public TaskEx
     void setAbortFlag(bool fatal);
 
     JSRuntime *runtime() { return cx_->runtime; }
 
     JSContext *acquireContext() { PR_Lock(cxLock_); return cx_; }
     void releaseContext() { PR_Unlock(cxLock_); }
 };
 
-class js::AutoRendezvous
+class AutoEnterWarmup
+{
+    JSRuntime *runtime_;
+
+  public:
+    AutoEnterWarmup(JSRuntime *runtime) : runtime_(runtime) { runtime_->parallelWarmup++; }
+    ~AutoEnterWarmup() { runtime_->parallelWarmup--; }
+};
+
+class AutoRendezvous
 {
   private:
     ForkJoinSlice &threadCx;
 
   public:
     AutoRendezvous(ForkJoinSlice &threadCx)
         : threadCx(threadCx)
     {
         threadCx.shared->initiateRendezvous(threadCx);
     }
 
     ~AutoRendezvous() {
         threadCx.shared->endRendezvous(threadCx);
     }
 };
 
-class js::AutoSetForkJoinSlice
+class AutoSetForkJoinSlice
 {
   public:
     AutoSetForkJoinSlice(ForkJoinSlice *threadCx) {
         PR_SetThreadPrivate(ForkJoinSlice::ThreadPrivateIndex, threadCx);
     }
 
     ~AutoSetForkJoinSlice() {
         PR_SetThreadPrivate(ForkJoinSlice::ThreadPrivateIndex, NULL);
     }
 };
 
+} // namespace js
+
+///////////////////////////////////////////////////////////////////////////
+// js::ForkJoin() and ParallelDo class
+//
+// These are the top-level objects that manage the parallel execution.
+// They handle parallel compilation (if necessary), triggering
+// parallel execution, and recovering from bailouts.
+
+bool
+js::ForkJoin(JSContext *cx, CallArgs &args)
+{
+    JS_ASSERT(args[0].isObject());
+    JS_ASSERT(args[0].toObject().isFunction());
+
+    RootedObject fun(cx, &args[0].toObject());
+    ParallelDo op(cx, fun);
+    ExecutionStatus status = op.apply();
+    if (status == ExecutionFatal)
+        return false;
+
+    if (args[1].isObject()) {
+        RootedObject feedback(cx, &args[1].toObject());
+        if (feedback && feedback->isFunction()) {
+            InvokeArgsGuard feedbackArgs;
+            if (!cx->stack.pushInvokeArgs(cx, 1, &feedbackArgs))
+                return false;
+            feedbackArgs.setCallee(ObjectValue(*feedback));
+            feedbackArgs.setThis(UndefinedValue());
+            if (status == ExecutionParallel)
+                feedbackArgs[0].setInt32(op.bailouts);
+            else
+                feedbackArgs[0] = cx->runtime->positiveInfinityValue;
+            if (!Invoke(cx, feedbackArgs))
+                return false;
+        }
+    }
+
+    return true;
+}
+
+js::ParallelDo::ParallelDo(JSContext *cx, HandleObject fun)
+  : bailouts(0),
+    pendingInvalidations_(cx),
+    cx_(cx),
+    fun_(fun)
+{ }
+
+ExecutionStatus
+js::ParallelDo::apply()
+{
+    SpewBeginOp(cx_, "ParallelDo");
+
+    if (!ion::IsEnabled(cx_))
+        return SpewEndOp(disqualifyFromParallelExecution());
+
+    if (!pendingInvalidations_.resize(ForkJoinSlices(cx_)))
+        return SpewEndOp(ExecutionFatal);
+
+    // Try to execute in parallel.  If a bailout occurs, re-warmup
+    // and then try again.  Repeat this a few times.
+    while (bailouts < MAX_BAILOUTS) {
+        MethodStatus status = compileForParallelExecution();
+        if (status == Method_Error)
+            return SpewEndOp(ExecutionFatal);
+        if (status != Method_Compiled)
+            return SpewEndOp(disqualifyFromParallelExecution());
+
+        ParallelResult result = executeInParallel();
+        switch (result) {
+          case TP_RETRY_AFTER_GC:
+            Spew(SpewBailouts, "Bailout due to GC request");
+            break;
+
+          case TP_RETRY_SEQUENTIALLY:
+            Spew(SpewBailouts, "Bailout not categorized");
+            break;
+
+          case TP_SUCCESS:
+            return SpewEndOp(ExecutionParallel);
+
+          case TP_FATAL:
+            return SpewEndOp(ExecutionFatal);
+        }
+
+        bailouts += 1;
+
+        SpewBailout(bailouts);
+
+        if (!invalidateBailedOutScripts())
+            return SpewEndOp(ExecutionFatal);
+
+        if (!warmupForParallelExecution())
+            return SpewEndOp(ExecutionFatal);
+    }
+
+    // After enough tries, just execute sequentially.
+    return SpewEndOp(disqualifyFromParallelExecution());
+}
+
+bool
+js::ParallelDo::executeSequentially()
+{
+    RootedValue funVal(cx_, ObjectValue(*fun_));
+    return ExecuteSequentially(cx_, funVal);
+}
+
+MethodStatus
+js::ParallelDo::compileForParallelExecution()
+{
+    // The kernel should be a self-hosted function.
+    if (!fun_->isFunction())
+        return Method_Skipped;
+
+    RootedFunction callee(cx_, fun_->toFunction());
+
+    if (!callee->isInterpreted() || !callee->isSelfHostedBuiltin())
+        return Method_Skipped;
+
+    if (callee->isInterpretedLazy() && !callee->initializeLazyScript(cx_))
+        return Method_Error;
+
+    // If this function has not been run enough to enable parallel
+    // execution, perform a warmup.
+    RootedScript script(cx_, callee->nonLazyScript());
+    if (script->getUseCount() < js_IonOptions.usesBeforeCompileParallel) {
+        if (!warmupForParallelExecution())
+            return Method_Error;
+    }
+
+    if (script->hasParallelIonScript() &&
+        !script->parallelIonScript()->hasInvalidatedCallTarget())
+    {
+        Spew(SpewOps, "Already compiled");
+        return Method_Compiled;
+    }
+
+    Spew(SpewOps, "Compiling all reachable functions");
+
+    ParallelCompileContext compileContext(cx_);
+    if (!compileContext.appendToWorklist(script))
+        return Method_Error;
+
+    MethodStatus status = compileContext.compileTransitively();
+    if (status != Method_Compiled)
+        return status;
+
+    // It can happen that during transitive compilation, our
+    // callee's parallel ion script is invalidated or GC'd. So
+    // before we declare success, double check that it's still
+    // compiled!
+    if (!script->hasParallelIonScript())
+        return Method_Skipped;
+
+    return Method_Compiled;
+}
+
+ExecutionStatus
+js::ParallelDo::disqualifyFromParallelExecution()
+{
+    if (!executeSequentially())
+        return ExecutionFatal;
+    return ExecutionSequential;
+}
+
+bool
+js::ParallelDo::invalidateBailedOutScripts()
+{
+    RootedScript script(cx_, fun_->toFunction()->nonLazyScript());
+
+    // Sometimes the script is collected or invalidated already,
+    // for example when a full GC runs at an inconvenient time.
+    if (!script->hasParallelIonScript()) {
+        JS_ASSERT(hasNoPendingInvalidations());
+        return true;
+    }
+
+    Vector<types::RecompileInfo> invalid(cx_);
+    for (uint32_t i = 0; i < pendingInvalidations_.length(); i++) {
+        JSScript *script = pendingInvalidations_[i];
+        if (script && !hasScript(invalid, script)) {
+            JS_ASSERT(script->hasParallelIonScript());
+            if (!invalid.append(script->parallelIonScript()->recompileInfo()))
+                return false;
+        }
+        pendingInvalidations_[i] = NULL;
+    }
+    Invalidate(cx_, invalid);
+    return true;
+}
+
+bool
+js::ParallelDo::warmupForParallelExecution()
+{
+    AutoEnterWarmup warmup(cx_->runtime);
+    return executeSequentially();
+}
+
+class AutoEnterParallelSection
+{
+  private:
+    JSContext *cx_;
+    uint8_t *prevIonTop_;
+
+  public:
+    AutoEnterParallelSection(JSContext *cx)
+      : cx_(cx),
+        prevIonTop_(cx->mainThread().ionTop)
+    {
+        // Note: we do not allow GC during parallel sections.
+        // Moreover, we do not wish to worry about making
+        // write barriers thread-safe.  Therefore, we guarantee
+        // that there is no incremental GC in progress:
+
+        if (JS::IsIncrementalGCInProgress(cx->runtime)) {
+            JS::PrepareForIncrementalGC(cx->runtime);
+            JS::FinishIncrementalGC(cx->runtime, JS::gcreason::API);
+        }
+
+        cx->runtime->gcHelperThread.waitBackgroundSweepEnd();
+    }
+
+    ~AutoEnterParallelSection() {
+        cx_->mainThread().ionTop = prevIonTop_;
+    }
+};
+
+ParallelResult
+js::ParallelDo::executeInParallel()
+{
+    // Recursive use of the ThreadPool is not supported.
+    if (ForkJoinSlice::Current() != NULL)
+        return TP_RETRY_SEQUENTIALLY;
+
+    AutoEnterParallelSection enter(cx_);
+
+    ThreadPool *threadPool = &cx_->runtime->threadPool;
+    uint32_t numSlices = ForkJoinSlices(cx_);
+
+    RootedObject rootedFun(cx_, fun_);
+    ForkJoinShared shared(cx_, threadPool, rootedFun, numSlices, numSlices - 1,
+                          pendingInvalidations_);
+    if (!shared.init())
+        return TP_RETRY_SEQUENTIALLY;
+
+    return shared.execute();
+}
+
+/*static*/ bool
+js::ParallelDo::hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script)
+{
+    for (uint32_t i = 0; i < scripts.length(); i++) {
+        if (scripts[i] == script->parallelIonScript()->recompileInfo())
+            return true;
+    }
+    return false;
+}
+
+bool
+js::ParallelDo::hasNoPendingInvalidations() {
+    for (uint32_t i = 0; i < pendingInvalidations_.length(); i++) {
+        if (pendingInvalidations_[i] != NULL)
+            return false;
+    }
+    return true;
+}
+
+// Can only enter callees with a valid IonScript.
+template <uint32_t maxArgc>
+class ParallelIonInvoke
+{
+    EnterIonCode enter_;
+    void *jitcode_;
+    void *calleeToken_;
+    Value argv_[maxArgc + 2];
+    uint32_t argc_;
+
+  public:
+    Value *args;
+
+    ParallelIonInvoke(JSCompartment *compartment,
+                      HandleFunction callee,
+                      uint32_t argc)
+      : argc_(argc),
+        args(argv_ + 2)
+    {
+        JS_ASSERT(argc <= maxArgc + 2);
+
+        // Set 'callee' and 'this'.
+        argv_[0] = ObjectValue(*callee);
+        argv_[1] = UndefinedValue();
+
+        // Find JIT code pointer.
+        IonScript *ion = callee->nonLazyScript()->parallelIonScript();
+        IonCode *code = ion->method();
+        jitcode_ = code->raw();
+        enter_ = compartment->ionCompartment()->enterJIT();
+        calleeToken_ = CalleeToParallelToken(callee);
+    }
+
+    bool invoke(PerThreadData *perThread) {
+        RootedValue result(perThread);
+        enter_(jitcode_, argc_ + 1, argv_ + 1, NULL, calleeToken_, NULL, 0, result.address());
+        return !result.isMagic();
+    }
+};
+
 /////////////////////////////////////////////////////////////////////////////
 // ForkJoinShared
 //
 
 ForkJoinShared::ForkJoinShared(JSContext *cx,
                                ThreadPool *threadPool,
-                               ForkJoinOp &op,
+                               HandleObject fun,
                                uint32_t numSlices,
-                               uint32_t uncompleted)
+                               uint32_t uncompleted,
+                               AutoScriptVector &pendingInvalidations)
   : cx_(cx),
     threadPool_(threadPool),
-    op_(op),
+    fun_(fun),
     numSlices_(numSlices),
     rendezvousEnd_(NULL),
     cxLock_(NULL),
+    pendingInvalidations_(pendingInvalidations),
     allocators_(cx),
     uncompleted_(uncompleted),
     blocked_(0),
     rendezvousIndex_(0),
     gcRequested_(false),
     gcReason_(JS::gcreason::NUM_REASONS),
     gcZone_(NULL),
     abort_(false),
     fatal_(false),
     rendezvous_(false)
-{ }
+{
+}
 
 bool
 ForkJoinShared::init()
 {
     // Create temporary arenas to hold the data allocated during the
     // parallel code.
     //
     // Note: you might think (as I did, initially) that we could use
@@ -320,22 +796,61 @@ ForkJoinShared::executeFromMainThread()
 {
     executePortion(&cx_->mainThread(), numSlices_ - 1);
 }
 
 void
 ForkJoinShared::executePortion(PerThreadData *perThread,
                                uint32_t threadId)
 {
+    // WARNING: This code runs ON THE PARALLEL WORKER THREAD.
+    // Therefore, it should NOT access `cx_` in any way!
+
     Allocator *allocator = allocators_[threadId];
     ForkJoinSlice slice(perThread, threadId, numSlices_, allocator, this);
     AutoSetForkJoinSlice autoContext(&slice);
 
-    if (!op_.parallel(slice))
+    Spew(SpewOps, "Up");
+
+    // Make a new IonContext for the slice, which is needed if we need to
+    // re-enter the VM.
+    IonContext icx(cx_->compartment, NULL);
+
+    JS_ASSERT(pendingInvalidations_[slice.sliceId] == NULL);
+
+    RootedObject fun(perThread, fun_);
+    JS_ASSERT(fun->isFunction());
+    RootedFunction callee(perThread, fun->toFunction());
+    if (!callee->nonLazyScript()->hasParallelIonScript()) {
+        // Sometimes, particularly with GCZeal, the parallel ion
+        // script can be collected between starting the parallel
+        // op and reaching this point.  In that case, we just fail
+        // and fallback.
+        Spew(SpewOps, "Down (Script no longer present)");
         setAbortFlag(false);
+    } else {
+        ParallelIonInvoke<3> fii(cx_->compartment, callee, 3);
+
+        fii.args[0] = Int32Value(slice.sliceId);
+        fii.args[1] = Int32Value(slice.numSlices);
+        fii.args[2] = BooleanValue(false);
+
+        bool ok = fii.invoke(perThread);
+        JS_ASSERT(ok == !slice.abortedScript);
+        if (!ok) {
+            JSScript *script = slice.abortedScript;
+            Spew(SpewBailouts, "Aborted script: %p (hasParallelIonScript? %d)",
+                 script, script->hasParallelIonScript());
+            JS_ASSERT(script->hasParallelIonScript());
+            pendingInvalidations_[slice.sliceId] = script;
+            setAbortFlag(false);
+        }
+    }
+
+    Spew(SpewOps, "Down");
 }
 
 bool
 ForkJoinShared::check(ForkJoinSlice &slice)
 {
     JS_ASSERT(cx_->runtime->interrupt);
 
     if (abort_)
@@ -545,146 +1060,357 @@ ForkJoinSlice::InitializeTLS()
     }
     return true;
 }
 
 void
 ForkJoinSlice::requestGC(JS::gcreason::Reason reason)
 {
     shared->requestGC(reason);
-    triggerAbort();
+    shared->setAbortFlag(false);
 }
 
 void
 ForkJoinSlice::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
 {
     shared->requestZoneGC(zone, reason);
-    triggerAbort();
-}
-
-void
-ForkJoinSlice::triggerAbort()
-{
     shared->setAbortFlag(false);
-
-    // set iontracklimit to -1 so that on next entry to a function,
-    // the thread will trigger the overrecursedcheck.  If the thread
-    // is in a loop, then it will be calling ForkJoinSlice::check(),
-    // in which case it will notice the shared abort_ flag.
-    //
-    // In principle, we probably ought to set the ionStackLimit's for
-    // the other threads too, but right now the various slice objects
-    // are not on a central list so that's not possible.
-
-    // Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the
-    // lock has not been initialized in these cases.
-    perThreadData->ionStackLimit = -1;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 
-namespace js {
-class AutoEnterParallelSection
-{
-  private:
-    JSContext *cx_;
-    uint8_t *prevIonTop_;
-
-  public:
-    AutoEnterParallelSection(JSContext *cx)
-      : cx_(cx),
-        prevIonTop_(cx->mainThread().ionTop)
-    {
-        // Note: we do not allow GC during parallel sections.
-        // Moreover, we do not wish to worry about making
-        // write barriers thread-safe.  Therefore, we guarantee
-        // that there is no incremental GC in progress.
-
-        if (JS::IsIncrementalGCInProgress(cx->runtime)) {
-            JS::PrepareForIncrementalGC(cx->runtime);
-            JS::FinishIncrementalGC(cx->runtime, JS::gcreason::API);
-        }
-
-        cx->runtime->gcHelperThread.waitBackgroundSweepEnd();
-    }
-
-    ~AutoEnterParallelSection() {
-        cx_->runtime->mainThread.ionTop = prevIonTop_;
-    }
-};
-} /* namespace js */
-
 uint32_t
 js::ForkJoinSlices(JSContext *cx)
 {
     // Parallel workers plus this main thread.
     return cx->runtime->threadPool.numWorkers() + 1;
 }
 
-ParallelResult
-js::ExecuteForkJoinOp(JSContext *cx, ForkJoinOp &op)
-{
-    // Recursive use of the ThreadPool is not supported.
-    JS_ASSERT(!InParallelSection());
+/////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
 
-    AutoEnterParallelSection enter(cx);
+//
+// Debug spew
+//
+
+#ifdef DEBUG
 
-    ThreadPool *threadPool = &cx->runtime->threadPool;
-    uint32_t numSlices = ForkJoinSlices(cx);
+static const char *
+ExecutionStatusToString(ExecutionStatus status)
+{
+    switch (status) {
+      case ExecutionFatal:
+        return "fatal";
+      case ExecutionSequential:
+        return "sequential";
+      case ExecutionParallel:
+        return "parallel";
+    }
+    return "(unknown status)";
+}
 
-    ForkJoinShared shared(cx, threadPool, op, numSlices, numSlices - 1);
-    if (!shared.init())
-        return TP_RETRY_SEQUENTIALLY;
-
-    return shared.execute();
+static const char *
+MethodStatusToString(MethodStatus status)
+{
+    switch (status) {
+      case Method_Error:
+        return "error";
+      case Method_CantCompile:
+        return "can't compile";
+      case Method_Skipped:
+        return "skipped";
+      case Method_Compiled:
+        return "compiled";
+    }
+    return "(unknown status)";
 }
 
-#else
+static const size_t BufferSize = 4096;
+
+class ParallelSpewer
+{
+    uint32_t depth;
+    bool colorable;
+    bool active[NumSpewChannels];
+
+    const char *color(const char *colorCode) {
+        if (!colorable)
+            return "";
+        return colorCode;
+    }
+
+    const char *reset() { return color("\x1b[0m"); }
+    const char *bold() { return color("\x1b[1m"); }
+    const char *red() { return color("\x1b[31m"); }
+    const char *green() { return color("\x1b[32m"); }
+    const char *yellow() { return color("\x1b[33m"); }
+    const char *cyan() { return color("\x1b[36m"); }
+    const char *sliceColor(uint32_t id) {
+        static const char *colors[] = {
+            "\x1b[7m\x1b[31m", "\x1b[7m\x1b[32m", "\x1b[7m\x1b[33m",
+            "\x1b[7m\x1b[34m", "\x1b[7m\x1b[35m", "\x1b[7m\x1b[36m",
+            "\x1b[7m\x1b[37m",
+            "\x1b[31m", "\x1b[32m", "\x1b[33m",
+            "\x1b[34m", "\x1b[35m", "\x1b[36m",
+            "\x1b[37m"
+        };
+        return color(colors[id % 14]);
+    }
+
+  public:
+    ParallelSpewer()
+      : depth(0)
+    {
+        const char *env;
+
+        mozilla::PodArrayZero(active);
+        env = getenv("PAFLAGS");
+        if (env) {
+            if (strstr(env, "ops"))
+                active[SpewOps] = true;
+            if (strstr(env, "compile"))
+                active[SpewCompile] = true;
+            if (strstr(env, "bailouts"))
+                active[SpewBailouts] = true;
+            if (strstr(env, "full")) {
+                for (uint32_t i = 0; i < NumSpewChannels; i++)
+                    active[i] = true;
+            }
+        }
+
+        env = getenv("TERM");
+        if (env) {
+            if (strcmp(env, "xterm-color") == 0 || strcmp(env, "xterm-256color") == 0)
+                colorable = true;
+        }
+    }
+
+    bool isActive(SpewChannel channel) {
+        return active[channel];
+    }
+
+    void spewVA(SpewChannel channel, const char *fmt, va_list ap) {
+        if (!active[channel])
+            return;
+
+        // Print into a buffer first so we use one fprintf, which usually
+        // doesn't get interrupted when running with multiple threads.
+        char buf[BufferSize];
+
+        if (ForkJoinSlice *slice = ForkJoinSlice::Current()) {
+            PR_snprintf(buf, BufferSize, "[%sParallel:%u%s] ",
+                        sliceColor(slice->sliceId), slice->sliceId, reset());
+        } else {
+            PR_snprintf(buf, BufferSize, "[Parallel:M] ");
+        }
+
+        for (uint32_t i = 0; i < depth; i++)
+            PR_snprintf(buf + strlen(buf), BufferSize, "  ");
+
+        PR_vsnprintf(buf + strlen(buf), BufferSize, fmt, ap);
+        PR_snprintf(buf + strlen(buf), BufferSize, "\n");
+
+        fprintf(stderr, "%s", buf);
+    }
+
+    void spew(SpewChannel channel, const char *fmt, ...) {
+        va_list ap;
+        va_start(ap, fmt);
+        spewVA(channel, fmt, ap);
+        va_end(ap);
+    }
+
+    void beginOp(JSContext *cx, const char *name) {
+        if (!active[SpewOps])
+            return;
+
+        if (cx) {
+            jsbytecode *pc;
+            JSScript *script = cx->stack.currentScript(&pc);
+            if (script && pc) {
+                NonBuiltinScriptFrameIter iter(cx);
+                if (iter.done()) {
+                    spew(SpewOps, "%sBEGIN %s%s (%s:%u)", bold(), name, reset(),
+                         script->filename(), PCToLineNumber(script, pc));
+                } else {
+                    spew(SpewOps, "%sBEGIN %s%s (%s:%u -> %s:%u)", bold(), name, reset(),
+                         iter.script()->filename(), PCToLineNumber(iter.script(), iter.pc()),
+                         script->filename(), PCToLineNumber(script, pc));
+                }
+            } else {
+                spew(SpewOps, "%sBEGIN %s%s", bold(), name, reset());
+            }
+        } else {
+            spew(SpewOps, "%sBEGIN %s%s", bold(), name, reset());
+        }
+
+        depth++;
+    }
+
+    void endOp(ExecutionStatus status) {
+        if (!active[SpewOps])
+            return;
+
+        JS_ASSERT(depth > 0);
+        depth--;
+
+        const char *statusColor;
+        switch (status) {
+          case ExecutionFatal:
+            statusColor = red();
+            break;
+          case ExecutionSequential:
+            statusColor = yellow();
+            break;
+          case ExecutionParallel:
+            statusColor = green();
+            break;
+          default:
+            statusColor = reset();
+            break;
+        }
+
+        spew(SpewOps, "%sEND %s%s%s", bold(),
+             statusColor, ExecutionStatusToString(status), reset());
+    }
+
+    void bailout(uint32_t count) {
+        if (!active[SpewOps])
+            return;
+
+        spew(SpewOps, "%s%sBAILOUT %d%s", bold(), yellow(), count, reset());
+    }
+
+    void beginCompile(HandleScript script) {
+        if (!active[SpewCompile])
+            return;
+
+        spew(SpewCompile, "COMPILE %p:%s:%u", script.get(), script->filename(), script->lineno);
+        depth++;
+    }
+
+    void endCompile(MethodStatus status) {
+        if (!active[SpewCompile])
+            return;
+
+        JS_ASSERT(depth > 0);
+        depth--;
+
+        const char *statusColor;
+        switch (status) {
+          case Method_Error:
+          case Method_CantCompile:
+            statusColor = red();
+            break;
+          case Method_Skipped:
+            statusColor = yellow();
+            break;
+          case Method_Compiled:
+            statusColor = green();
+            break;
+          default:
+            statusColor = reset();
+            break;
+        }
+
+        spew(SpewCompile, "END %s%s%s", statusColor, MethodStatusToString(status), reset());
+    }
+
+    void spewMIR(MDefinition *mir, const char *fmt, va_list ap) {
+        if (!active[SpewCompile])
+            return;
+
+        char buf[BufferSize];
+        PR_vsnprintf(buf, BufferSize, fmt, ap);
+
+        JSScript *script = mir->block()->info().script();
+        spew(SpewCompile, "%s%s%s: %s (%s:%u)", cyan(), mir->opName(), reset(), buf,
+             script->filename(), PCToLineNumber(script, mir->trackedPc()));
+    }
+
+    void spewBailoutIR(uint32_t bblockId, uint32_t lirId,
+                       const char *lir, const char *mir, JSScript *script, jsbytecode *pc) {
+        if (!active[SpewBailouts])
+            return;
+
+        // If we didn't bail from a LIR/MIR but from a propagated parallel
+        // bailout, don't bother printing anything since we've printed it
+        // elsewhere.
+        if (mir && script) {
+            spew(SpewBailouts, "%sBailout%s: %s / %s%s%s (block %d lir %d) (%s:%u)", yellow(), reset(),
+                 lir, cyan(), mir, reset(),
+                 bblockId, lirId,
+                 script->filename(), PCToLineNumber(script, pc));
+        }
+    }
+};
+
+// Singleton instance of the spewer.
+static ParallelSpewer spewer;
 
 bool
-ForkJoinSlice::isMainThread()
-{
-    return true;
-}
-
-JSRuntime *
-ForkJoinSlice::runtime()
+parallel::SpewEnabled(SpewChannel channel)
 {
-    return NULL;
-}
-
-bool
-ForkJoinSlice::check()
-{
-    return false;
-}
-
-bool
-ForkJoinSlice::InitializeTLS()
-{
-    return true;
+    return spewer.isActive(channel);
 }
 
 void
-ForkJoinSlice::requestGC(JS::gcreason::Reason reason)
+parallel::Spew(SpewChannel channel, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    spewer.spewVA(channel, fmt, ap);
+    va_end(ap);
+}
+
+void
+parallel::SpewBeginOp(JSContext *cx, const char *name)
 {
-    JS_NOT_REACHED("No threadsafe, no ion");
+    spewer.beginOp(cx, name);
+}
+
+ExecutionStatus
+parallel::SpewEndOp(ExecutionStatus status)
+{
+    spewer.endOp(status);
+    return status;
+}
+
+void
+parallel::SpewBailout(uint32_t count)
+{
+    spewer.bailout(count);
 }
 
 void
-ForkJoinSlice::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
+parallel::SpewBeginCompile(HandleScript script)
 {
-    JS_NOT_REACHED("No threadsafe, no ion");
+    spewer.beginCompile(script);
+}
+
+MethodStatus
+parallel::SpewEndCompile(MethodStatus status)
+{
+    spewer.endCompile(status);
+    return status;
 }
 
-uint32_t
-js::ForkJoinSlices(JSContext *cx)
+void
+parallel::SpewMIR(MDefinition *mir, const char *fmt, ...)
 {
-    return 1;
+    va_list ap;
+    va_start(ap, fmt);
+    spewer.spewMIR(mir, fmt, ap);
+    va_end(ap);
 }
 
-ParallelResult
-js::ExecuteForkJoinOp(JSContext *cx, ForkJoinOp &op)
+void
+parallel::SpewBailoutIR(uint32_t bblockId, uint32_t lirId,
+                        const char *lir, const char *mir,
+                        JSScript *script, jsbytecode *pc)
 {
-    return TP_RETRY_SEQUENTIALLY;
+    spewer.spewBailoutIR(bblockId, lirId, lir, mir, script, pc);
 }
 
-#endif // defined(JS_THREADSAFE) && defined(JS_ION)
+#endif // DEBUG
+
+#endif // JS_THREADSAFE && JS_ION
--- a/js/src/vm/ForkJoin.h
+++ b/js/src/vm/ForkJoin.h
@@ -2,49 +2,123 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ForkJoin_h__
 #define ForkJoin_h__
 
+#include "jscntxt.h"
 #include "vm/ThreadPool.h"
 #include "jsgc.h"
+#include "ion/Ion.h"
 
-// ForkJoin
+///////////////////////////////////////////////////////////////////////////
+// Read Me First
+//
+// The ForkJoin abstraction:
+// -------------------------
 //
 // This is the building block for executing multi-threaded JavaScript with
 // shared memory (as distinct from Web Workers).  The idea is that you have
 // some (typically data-parallel) operation which you wish to execute in
-// parallel across as many threads as you have available.  An example might be
-// applying |map()| to a vector in parallel. To implement such a thing, you
-// would define a subclass of |ForkJoinOp| to implement the operation and then
-// invoke |ExecuteForkJoinOp()|, as follows:
+// parallel across as many threads as you have available.
+//
+// The ForkJoin abstraction is intended to be used by self-hosted code
+// to enable parallel execution.  At the top-level, it consists of a native
+// function (exposed as the ForkJoin intrinsic) that is used like so:
 //
-//     class MyForkJoinOp {
-//       ... define callbacks as appropriate for your operation ...
-//     };
-//     MyForkJoinOp op;
-//     ExecuteForkJoinOp(cx, op);
+//     ForkJoin(func, feedback)
 //
-// |ExecuteForkJoinOp()| will fire up the workers in the runtime's
-// thread pool, have them execute the callback |parallel()| defined in
-// the |ForkJoinOp| class, and then return once all the workers have
-// completed.  You will receive |N| calls to the |parallel()|
-// callback, where |N| is the value returned by |ForkJoinSlice()|.
-// Each callback will be supplied with a |ForkJoinSlice| instance
-// providing some context.
+// The intention of this statement is to start N copies of |func()|
+// running in parallel.  Each copy will then do 1/Nth of the total
+// work.  Here N is number of workers in the threadpool (see
+// ThreadPool.h---by default, N is the number of cores on the
+// computer).
 //
 // Typically there will be one call to |parallel()| from each worker thread,
 // but that is not something you should rely upon---if we implement
 // work-stealing, for example, then it could be that a single worker thread
 // winds up handling multiple slices.
 //
-// Operation callback:
+// The second argument, |feedback|, is an optional callback that will
+// receiver information about how execution proceeded.  This is
+// intended for use in unit testing but also for providing feedback to
+// users.  Note that gathering the data to provide to |feedback| is
+// not free and so execution will run somewhat slower if |feedback| is
+// provided.
+//
+// func() should expect the following arguments:
+//
+//     func(id, n, warmup)
+//
+// Here, |id| is the slice id. |n| is the total number of slices.  The
+// parameter |warmup| is true for a *warmup or recovery phase*.
+// Warmup phases are discussed below in more detail, but the general
+// idea is that if |warmup| is true, |func| should only do a fixed
+// amount of work.  If |warmup| is false, |func| should try to do all
+// remaining work is assigned.
+//
+// Note that we implicitly assume that |func| is tracking how much
+// work it has accomplished thus far; some techniques for doing this
+// are discussed in |ParallelArray.js|.
+//
+// Warmups and Sequential Fallbacks
+// --------------------------------
+//
+// ForkJoin can only execute code in parallel when it has been
+// ion-compiled in Parallel Execution Mode. ForkJoin handles this part
+// for you. However, because ion relies on having decent type
+// information available, it is necessary to run the code sequentially
+// for a few iterations first to get the various type sets "primed"
+// with reasonable information.  We try to make do with just a few
+// runs, under the hypothesis that parallel execution code which reach
+// type stability relatively quickly.
+//
+// The general strategy of ForkJoin is as follows:
+//
+// - If the code has not yet been run, invoke `func` sequentially with
+//   warmup set to true.  When warmup is true, `func` should try and
+//   do less work than normal---just enough to prime type sets. (See
+//   ParallelArray.js for a discussion of specifically how we do this
+//   in the case of ParallelArray).
+//
+// - Try to execute the code in parallel.  Parallel execution mode has
+//   three possible results: success, fatal error, or bailout.  If a
+//   bailout occurs, it means that the code attempted some action
+//   which is not possible in parallel mode.  This might be a
+//   modification to shared state, but it might also be that it
+//   attempted to take some theoreticaly pure action that has not been
+//   made threadsafe (yet?).
+//
+// - If parallel execution is successful, ForkJoin returns true.
+//
+// - If parallel execution results in a fatal error, ForkJoin returns false.
+//
+// - If parallel execution results in a *bailout*, this is when things
+//   get interesting.  In that case, the semantics of parallel
+//   execution guarantee us that no visible side effects have occurred
+//   (unless they were performed with the intrinsic
+//   |UnsafeSetElement()|, which can only be used in self-hosted
+//   code).  We therefore reinvoke |func()| but with warmup set to
+//   true.  The idea here is that often parallel bailouts result from
+//   a failed type guard or other similar assumption, so rerunning the
+//   warmup sequentially gives us a chance to recompile with more
+//   data.  Because warmup is true, we do not expect this sequential
+//   call to process all remaining data, just a chunk.  After this
+//   recovery execution is complete, we again attempt parallel
+//   execution.
+//
+// - If more than a fixed number of bailouts occur, we give up on
+//   parallelization and just invoke |func()| N times in a row (once
+//   for each worker) but with |warmup| set to false.
+//
+// Operation callback
+// ------------------
 //
 // During parallel execution, you should periodically invoke |slice.check()|,
 // which will handle the operation callback.  If the operation callback is
 // necessary, |slice.check()| will arrange a rendezvous---that is, as each
 // active worker invokes |check()|, it will come to a halt until everyone is
 // blocked (Stop The World).  At this point, we perform the callback on the
 // main thread, and then resume execution.  If a worker thread terminates
 // before calling |check()|, that's fine too.  We assume that you do not do
@@ -93,54 +167,47 @@
 //
 // - The API does not support recursive or nested use.  That is, the
 //   |parallel()| callback of a |ForkJoinOp| may not itself invoke
 //   |ExecuteForkJoinOp()|.  We may lift this limitation in the future.
 //
 // - No load balancing is performed between worker threads.  That means that
 //   the fork-join system is best suited for problems that can be slice into
 //   uniform bits.
+///////////////////////////////////////////////////////////////////////////
 
 namespace js {
 
-// Parallel operations in general can have one of three states.  They may
-// succeed, fail, or "bail", where bail indicates that the code encountered an
-// unexpected condition and should be re-run sequentially.
-enum ParallelResult { TP_SUCCESS, TP_RETRY_SEQUENTIALLY, TP_FATAL };
+struct ForkJoinShared;
+struct ForkJoinSlice;
 
-struct ForkJoinOp;
+bool ForkJoin(JSContext *cx, CallArgs &args);
 
 // Returns the number of slices that a fork-join op will have when
 // executed.
-uint32_t
-ForkJoinSlices(JSContext *cx);
-
-// Executes the given |TaskSet| in parallel using the runtime's |ThreadPool|,
-// returning upon completion.  In general, if there are |N| workers in the
-// threadpool, the problem will be divided into |N+1| slices, as the main
-// thread will also execute one slice.
-ParallelResult ExecuteForkJoinOp(JSContext *cx, ForkJoinOp &op);
-
-class PerThreadData;
-class ForkJoinShared;
-class AutoRendezvous;
-class AutoSetForkJoinSlice;
+uint32_t ForkJoinSlices(JSContext *cx);
 
 #ifdef DEBUG
 struct IonLIRTraceData {
     uint32_t bblock;
     uint32_t lir;
     uint32_t execModeInt;
     const char *lirOpName;
     const char *mirOpName;
     JSScript *script;
     jsbytecode *pc;
 };
 #endif
 
+// Parallel operations in general can have one of three states.  They may
+// succeed, fail, or "bail", where bail indicates that the code encountered an
+// unexpected condition and should be re-run sequentially.
+// Different subcategories of the "bail" state are encoded as variants of
+// TP_RETRY_*.
+enum ParallelResult { TP_SUCCESS, TP_RETRY_SEQUENTIALLY, TP_RETRY_AFTER_GC, TP_FATAL };
 struct ForkJoinSlice
 {
   public:
     // PerThreadData corresponding to the current worker thread.
     PerThreadData *perThreadData;
 
     // Which slice should you process? Ranges from 0 to |numSlices|.
     const uint32_t sliceId;
@@ -211,38 +278,19 @@ struct ForkJoinSlice
     friend class AutoSetForkJoinSlice;
 
 #if defined(JS_THREADSAFE) && defined(JS_ION)
     // Initialized by InitializeTLS()
     static unsigned ThreadPrivateIndex;
     static bool TLSInitialized;
 #endif
 
-#if defined(JS_THREADSAFE) && defined(JS_ION)
-    // Sets the abort flag and adjusts ionStackLimit so as to cause
-    // the overrun check to fail.  This should lead to the operation
-    // as a whole aborting.
-    void triggerAbort();
-#endif
-
     ForkJoinShared *const shared;
 };
 
-// Generic interface for specifying divisible operations that can be
-// executed in a fork-join fashion.
-struct ForkJoinOp
-{
-  public:
-    // Invoked from each parallel thread to process one slice.  The
-    // |ForkJoinSlice| which is supplied will also be available using TLS.
-    //
-    // Returns true on success, false to halt parallel execution.
-    virtual bool parallel(ForkJoinSlice &slice) = 0;
-};
-
 // Locks a JSContext for its scope.
 class LockedJSContext
 {
 #if defined(JS_THREADSAFE) && defined(JS_ION)
     ForkJoinSlice *slice_;
 #endif
     JSContext *cx_;
 
@@ -264,19 +312,79 @@ class LockedJSContext
 
     operator JSContext *() { return cx_; }
     JSContext *operator->() { return cx_; }
 };
 
 static inline bool
 InParallelSection()
 {
-    return ForkJoinSlice::Current() != NULL;
+#ifdef JS_THREADSAFE
+    ForkJoinSlice *current = ForkJoinSlice::Current();
+    return current != NULL;
+#else
+    return false;
+#endif
 }
 
+///////////////////////////////////////////////////////////////////////////
+// Debug Spew
+
+namespace parallel {
+
+enum ExecutionStatus {
+    // Parallel or seq execution terminated in a fatal way, operation failed
+    ExecutionFatal,
+
+    // Parallel exec failed and so we fell back to sequential
+    ExecutionSequential,
+
+    // Parallel exec was successful after some number of bailouts
+    ExecutionParallel
+};
+
+enum SpewChannel {
+    SpewOps,
+    SpewCompile,
+    SpewBailouts,
+    NumSpewChannels
+};
+
+#if defined(DEBUG) && defined(JS_THREADSAFE) && defined(JS_ION)
+
+bool SpewEnabled(SpewChannel channel);
+void Spew(SpewChannel channel, const char *fmt, ...);
+void SpewBeginOp(JSContext *cx, const char *name);
+void SpewBailout(uint32_t count);
+ExecutionStatus SpewEndOp(ExecutionStatus status);
+void SpewBeginCompile(HandleScript script);
+ion::MethodStatus SpewEndCompile(ion::MethodStatus status);
+void SpewMIR(ion::MDefinition *mir, const char *fmt, ...);
+void SpewBailoutIR(uint32_t bblockId, uint32_t lirId,
+                   const char *lir, const char *mir, JSScript *script, jsbytecode *pc);
+
+#else
+
+static inline bool SpewEnabled(SpewChannel channel) { return false; }
+static inline void Spew(SpewChannel channel, const char *fmt, ...) { }
+static inline void SpewBeginOp(JSContext *cx, const char *name) { }
+static inline void SpewBailout(uint32_t count) {}
+static inline ExecutionStatus SpewEndOp(ExecutionStatus status) { return status; }
+static inline void SpewBeginCompile(HandleScript script) { }
+#ifdef JS_ION
+static inline ion::MethodStatus SpewEndCompile(ion::MethodStatus status) { return status; }
+static inline void SpewMIR(ion::MDefinition *mir, const char *fmt, ...) { }
+#endif
+static inline void SpewBailoutIR(uint32_t bblockId, uint32_t lirId,
+                                 const char *lir, const char *mir,
+                                 JSScript *script, jsbytecode *pc) { }
+
+#endif // DEBUG && JS_THREADSAFE && JS_ION
+
+} // namespace parallel
 } // namespace js
 
 /* static */ inline js::ForkJoinSlice *
 js::ForkJoinSlice::Current()
 {
 #if defined(JS_THREADSAFE) && defined(JS_ION)
     return (ForkJoinSlice*) PR_GetThreadPrivate(ThreadPrivateIndex);
 #else
deleted file mode 100644
--- a/js/src/vm/ParallelDo.cpp
+++ /dev/null
@@ -1,682 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/PodOperations.h"
-
-#include "jsapi.h"
-#include "jsobj.h"
-#include "jsarray.h"
-
-#include "vm/ForkJoin.h"
-#include "vm/GlobalObject.h"
-#include "vm/ParallelDo.h"
-#include "vm/String.h"
-#include "vm/ThreadPool.h"
-
-#include "jsinterpinlines.h"
-#include "jsobjinlines.h"
-
-#ifdef JS_ION
-#include "ion/ParallelArrayAnalysis.h"
-#endif // ION
-
-#if defined(DEBUG) && defined(JS_THREADSAFE) && defined(JS_ION)
-#include "ion/Ion.h"
-#include "ion/MIR.h"
-#include "ion/MIRGraph.h"
-#include "ion/IonCompartment.h"
-
-#include "prprf.h"
-#endif // DEBUG && THREADSAFE && ION
-
-using namespace js;
-using namespace js::parallel;
-using namespace js::ion;
-
-using mozilla::PodArrayZero;
-
-//
-// Debug spew
-//
-
-#if defined(DEBUG) && defined(JS_THREADSAFE) && defined(JS_ION)
-
-static const char *
-ExecutionStatusToString(ExecutionStatus status)
-{
-    switch (status) {
-      case ExecutionFatal:
-        return "fatal";
-      case ExecutionSequential:
-        return "sequential";
-      case ExecutionParallel:
-        return "parallel";
-    }
-    return "(unknown status)";
-}
-
-static const char *
-MethodStatusToString(MethodStatus status)
-{
-    switch (status) {
-      case Method_Error:
-        return "error";
-      case Method_CantCompile:
-        return "can't compile";
-      case Method_Skipped:
-        return "skipped";
-      case Method_Compiled:
-        return "compiled";
-    }
-    return "(unknown status)";
-}
-
-static const size_t BufferSize = 4096;
-
-class ParallelSpewer
-{
-    uint32_t depth;
-    bool colorable;
-    bool active[NumSpewChannels];
-
-    const char *color(const char *colorCode) {
-        if (!colorable)
-            return "";
-        return colorCode;
-    }
-
-    const char *reset() { return color("\x1b[0m"); }
-    const char *bold() { return color("\x1b[1m"); }
-    const char *red() { return color("\x1b[31m"); }
-    const char *green() { return color("\x1b[32m"); }
-    const char *yellow() { return color("\x1b[33m"); }
-    const char *cyan() { return color("\x1b[36m"); }
-    const char *sliceColor(uint32_t id) {
-        static const char *colors[] = {
-            "\x1b[7m\x1b[31m", "\x1b[7m\x1b[32m", "\x1b[7m\x1b[33m",
-            "\x1b[7m\x1b[34m", "\x1b[7m\x1b[35m", "\x1b[7m\x1b[36m",
-            "\x1b[7m\x1b[37m",
-            "\x1b[31m", "\x1b[32m", "\x1b[33m",
-            "\x1b[34m", "\x1b[35m", "\x1b[36m",
-            "\x1b[37m"
-        };
-        return color(colors[id % 14]);
-    }
-
-  public:
-    ParallelSpewer()
-      : depth(0)
-    {
-        const char *env;
-
-        PodArrayZero(active);
-        env = getenv("PAFLAGS");
-        if (env) {
-            if (strstr(env, "ops"))
-                active[SpewOps] = true;
-            if (strstr(env, "compile"))
-                active[SpewCompile] = true;
-            if (strstr(env, "bailouts"))
-                active[SpewBailouts] = true;
-            if (strstr(env, "full")) {
-                for (uint32_t i = 0; i < NumSpewChannels; i++)
-                    active[i] = true;
-            }
-        }
-
-        env = getenv("TERM");
-        if (env) {
-            if (strcmp(env, "xterm-color") == 0 || strcmp(env, "xterm-256color") == 0)
-                colorable = true;
-        }
-    }
-
-    bool isActive(SpewChannel channel) {
-        return active[channel];
-    }
-
-    void spewVA(SpewChannel channel, const char *fmt, va_list ap) {
-        if (!active[channel])
-            return;
-
-        // Print into a buffer first so we use one fprintf, which usually
-        // doesn't get interrupted when running with multiple threads.
-        char buf[BufferSize];
-
-        if (ForkJoinSlice *slice = ForkJoinSlice::Current()) {
-            PR_snprintf(buf, BufferSize, "[%sParallel:%u%s] ",
-                        sliceColor(slice->sliceId), slice->sliceId, reset());
-        } else {
-            PR_snprintf(buf, BufferSize, "[Parallel:M] ");
-        }
-
-        for (uint32_t i = 0; i < depth; i++)
-            PR_snprintf(buf + strlen(buf), BufferSize, "  ");
-
-        PR_vsnprintf(buf + strlen(buf), BufferSize, fmt, ap);
-        PR_snprintf(buf + strlen(buf), BufferSize, "\n");
-
-        fprintf(stderr, "%s", buf);
-    }
-
-    void spew(SpewChannel channel, const char *fmt, ...) {
-        va_list ap;
-        va_start(ap, fmt);
-        spewVA(channel, fmt, ap);
-        va_end(ap);
-    }
-
-    void beginOp(JSContext *cx, const char *name) {
-        if (!active[SpewOps])
-            return;
-
-        if (cx) {
-            jsbytecode *pc;
-            JSScript *script = cx->stack.currentScript(&pc);
-            if (script && pc) {
-                NonBuiltinScriptFrameIter iter(cx);
-                if (iter.done()) {
-                    spew(SpewOps, "%sBEGIN %s%s (%s:%u)", bold(), name, reset(),
-                         script->filename(), PCToLineNumber(script, pc));
-                } else {
-                    spew(SpewOps, "%sBEGIN %s%s (%s:%u -> %s:%u)", bold(), name, reset(),
-                         iter.script()->filename(), PCToLineNumber(iter.script(), iter.pc()),
-                         script->filename(), PCToLineNumber(script, pc));
-                }
-            } else {
-                spew(SpewOps, "%sBEGIN %s%s", bold(), name, reset());
-            }
-        } else {
-            spew(SpewOps, "%sBEGIN %s%s", bold(), name, reset());
-        }
-
-        depth++;
-    }
-
-    void endOp(ExecutionStatus status) {
-        if (!active[SpewOps])
-            return;
-
-        JS_ASSERT(depth > 0);
-        depth--;
-
-        const char *statusColor;
-        switch (status) {
-          case ExecutionFatal:
-            statusColor = red();
-            break;
-          case ExecutionSequential:
-            statusColor = yellow();
-            break;
-          case ExecutionParallel:
-            statusColor = green();
-            break;
-          default:
-            statusColor = reset();
-            break;
-        }
-
-        spew(SpewOps, "%sEND %s%s%s", bold(),
-             statusColor, ExecutionStatusToString(status), reset());
-    }
-
-    void bailout(uint32_t count) {
-        if (!active[SpewOps])
-            return;
-
-        spew(SpewOps, "%s%sBAILOUT %d%s", bold(), yellow(), count, reset());
-    }
-
-    void beginCompile(HandleScript script) {
-        if (!active[SpewCompile])
-            return;
-
-        spew(SpewCompile, "COMPILE %p:%s:%u", script.get(), script->filename(), script->lineno);
-        depth++;
-    }
-
-    void endCompile(MethodStatus status) {
-        if (!active[SpewCompile])
-            return;
-
-        JS_ASSERT(depth > 0);
-        depth--;
-
-        const char *statusColor;
-        switch (status) {
-          case Method_Error:
-          case Method_CantCompile:
-            statusColor = red();
-            break;
-          case Method_Skipped:
-            statusColor = yellow();
-            break;
-          case Method_Compiled:
-            statusColor = green();
-            break;
-          default:
-            statusColor = reset();
-            break;
-        }
-
-        spew(SpewCompile, "END %s%s%s", statusColor, MethodStatusToString(status), reset());
-    }
-
-    void spewMIR(MDefinition *mir, const char *fmt, va_list ap) {
-        if (!active[SpewCompile])
-            return;
-
-        char buf[BufferSize];
-        PR_vsnprintf(buf, BufferSize, fmt, ap);
-
-        JSScript *script = mir->block()->info().script();
-        spew(SpewCompile, "%s%s%s: %s (%s:%u)", cyan(), mir->opName(), reset(), buf,
-             script->filename(), PCToLineNumber(script, mir->trackedPc()));
-    }
-
-    void spewBailoutIR(uint32_t bblockId, uint32_t lirId,
-                       const char *lir, const char *mir, JSScript *script, jsbytecode *pc) {
-        if (!active[SpewBailouts])
-            return;
-
-        // If we didn't bail from a LIR/MIR but from a propagated parallel
-        // bailout, don't bother printing anything since we've printed it
-        // elsewhere.
-        if (mir && script) {
-            spew(SpewBailouts, "%sBailout%s: %s / %s%s%s (block %d lir %d) (%s:%u)", yellow(), reset(),
-                 lir, cyan(), mir, reset(),
-                 bblockId, lirId,
-                 script->filename(), PCToLineNumber(script, pc));
-        }
-    }
-};
-
-// Singleton instance of the spewer.
-static ParallelSpewer spewer;
-
-bool
-parallel::SpewEnabled(SpewChannel channel)
-{
-    return spewer.isActive(channel);
-}
-
-void
-parallel::Spew(SpewChannel channel, const char *fmt, ...)
-{
-    va_list ap;
-    va_start(ap, fmt);
-    spewer.spewVA(channel, fmt, ap);
-    va_end(ap);
-}
-
-void
-parallel::SpewBeginOp(JSContext *cx, const char *name)
-{
-    spewer.beginOp(cx, name);
-}
-
-ExecutionStatus
-parallel::SpewEndOp(ExecutionStatus status)
-{
-    spewer.endOp(status);
-    return status;
-}
-
-void
-parallel::SpewBailout(uint32_t count)
-{
-    spewer.bailout(count);
-}
-
-void
-parallel::SpewBeginCompile(HandleScript script)
-{
-    spewer.beginCompile(script);
-}
-
-MethodStatus
-parallel::SpewEndCompile(MethodStatus status)
-{
-    spewer.endCompile(status);
-    return status;
-}
-
-void
-parallel::SpewMIR(MDefinition *mir, const char *fmt, ...)
-{
-    va_list ap;
-    va_start(ap, fmt);
-    spewer.spewMIR(mir, fmt, ap);
-    va_end(ap);
-}
-
-void
-parallel::SpewBailoutIR(uint32_t bblockId, uint32_t lirId,
-                        const char *lir, const char *mir,
-                        JSScript *script, jsbytecode *pc)
-{
-    spewer.spewBailoutIR(bblockId, lirId, lir, mir, script, pc);
-}
-
-#endif // DEBUG && JS_THREADSAFE && JS_ION
-
-#ifdef JS_ION
-class AutoEnterWarmup
-{
-    JSRuntime *runtime_;
-
-  public:
-    AutoEnterWarmup(JSRuntime *runtime) : runtime_(runtime) { runtime_->parallelWarmup++; }
-    ~AutoEnterWarmup() { runtime_->parallelWarmup--; }
-};
-
-// Can only enter callees with a valid IonScript.
-template <uint32_t maxArgc>
-class ParallelIonInvoke
-{
-    EnterIonCode enter_;
-    void *jitcode_;
-    void *calleeToken_;
-    Value argv_[maxArgc + 2];
-    uint32_t argc_;
-
-  public:
-    Value *args;
-
-    ParallelIonInvoke(JSContext *cx, HandleFunction callee, uint32_t argc)
-      : argc_(argc),
-        args(argv_ + 2)
-    {
-        JS_ASSERT(argc <= maxArgc + 2);
-
-        // Set 'callee' and 'this'.
-        argv_[0] = ObjectValue(*callee);
-        argv_[1] = UndefinedValue();
-
-        // Find JIT code pointer.
-        IonScript *ion = callee->nonLazyScript()->parallelIonScript();
-        IonCode *code = ion->method();
-        jitcode_ = code->raw();
-        enter_ = cx->compartment->ionCompartment()->enterJIT();
-        calleeToken_ = CalleeToParallelToken(callee);
-    }
-
-    bool invoke() {
-        RootedValue result(TlsPerThreadData.get());
-        enter_(jitcode_, argc_ + 1, argv_ + 1, NULL, calleeToken_, NULL, 0, result.address());
-        return !result.isMagic();
-    }
-};
-#endif // JS_ION
-
-class ParallelDo : public ForkJoinOp
-{
-    JSContext *cx_;
-    RootedObject fun_;
-
-  public:
-    // For tests, make sure to keep this in sync with minItemsTestingThreshold.
-    const static uint32_t MAX_BAILOUTS = 3;
-    uint32_t bailouts;
-    AutoScriptVector pendingInvalidations;
-
-    ParallelDo(JSContext *cx, HandleObject fun)
-      : cx_(cx),
-        fun_(cx, fun),
-        bailouts(0),
-        pendingInvalidations(cx)
-    { }
-
-#ifndef JS_ION
-    ExecutionStatus apply() {
-        if (!executeSequentially())
-            return ExecutionFatal;
-        return ExecutionSequential;
-    }
-#else
-    ExecutionStatus apply() {
-        SpewBeginOp(cx_, "ParallelDo");
-
-        if (!ion::IsEnabled(cx_))
-            return SpewEndOp(disqualifyFromParallelExecution());
-
-        if (!pendingInvalidations.resize(ForkJoinSlices(cx_)))
-            return SpewEndOp(ExecutionFatal);
-
-        // Try to execute in parallel.  If a bailout occurs, re-warmup
-        // and then try again.  Repeat this a few times.
-        while (bailouts < MAX_BAILOUTS) {
-            MethodStatus status = compileForParallelExecution();
-            if (status == Method_Error)
-                return SpewEndOp(ExecutionFatal);
-            if (status != Method_Compiled)
-                return SpewEndOp(disqualifyFromParallelExecution());
-
-            ParallelResult result = js::ExecuteForkJoinOp(cx_, *this);
-            switch (result) {
-              case TP_RETRY_SEQUENTIALLY:
-                Spew(SpewBailouts, "Bailout not categorized");
-                break;
-
-              case TP_SUCCESS:
-                return SpewEndOp(ExecutionParallel);
-
-              case TP_FATAL:
-                return SpewEndOp(ExecutionFatal);
-            }
-
-            bailouts += 1;
-            SpewBailout(bailouts);
-
-            if (!invalidateBailedOutScripts())
-                return SpewEndOp(ExecutionFatal);
-
-            if (!warmupForParallelExecution())
-                return SpewEndOp(ExecutionFatal);
-        }
-
-        // After enough tries, just execute sequentially.
-        return SpewEndOp(disqualifyFromParallelExecution());
-    }
-
-    MethodStatus compileForParallelExecution() {
-        // The kernel should be a self-hosted function.
-        if (!fun_->isFunction())
-            return Method_Skipped;
-
-        RootedFunction callee(TlsPerThreadData.get(), fun_->toFunction());
-
-        if (!callee->isInterpreted() || !callee->isSelfHostedBuiltin())
-            return Method_Skipped;
-
-        if (callee->isInterpretedLazy() && !callee->initializeLazyScript(cx_))
-            return Method_Error;
-
-        // If this function has not been run enough to enable parallel
-        // execution, perform a warmup.
-        RootedScript script(TlsPerThreadData.get(), callee->nonLazyScript());
-        if (script->getUseCount() < js_IonOptions.usesBeforeCompileParallel) {
-            if (!warmupForParallelExecution())
-                return Method_Error;
-        }
-
-        if (script->hasParallelIonScript() &&
-            !script->parallelIonScript()->hasInvalidatedCallTarget())
-        {
-            Spew(SpewOps, "Already compiled");
-            return Method_Compiled;
-        }
-
-        Spew(SpewOps, "Compiling all reachable functions");
-
-        ParallelCompileContext compileContext(cx_);
-        if (!compileContext.appendToWorklist(script))
-            return Method_Error;
-
-        MethodStatus status = compileContext.compileTransitively();
-        if (status != Method_Compiled)
-            return status;
-
-        // it can happen that during transitive compilation, our
-        // callee's parallel ion script is invalidated or GC'd. So
-        // before we declare success, double check that it's still
-        // compiled!
-        if (!callee->nonLazyScript()->hasParallelIonScript())
-            return Method_Skipped;
-
-        return Method_Compiled;
-    }
-
-    ExecutionStatus disqualifyFromParallelExecution() {
-        if (!executeSequentially())
-            return ExecutionFatal;
-        return ExecutionSequential;
-    }
-
-    bool invalidateBailedOutScripts() {
-        RootedScript script(TlsPerThreadData.get(), fun_->toFunction()->nonLazyScript());
-
-        // Sometimes the script is collected or invalidated already,
-        // for example when a full GC runs at an inconvenient time.
-        if (!script->hasParallelIonScript()) {
-            JS_ASSERT(hasNoPendingInvalidations());
-            return true;
-        }
-
-        Vector<types::RecompileInfo> invalid(cx_);
-        for (uint32_t i = 0; i < pendingInvalidations.length(); i++) {
-            JSScript *script = pendingInvalidations[i];
-            if (script && !hasScript(invalid, script)) {
-                JS_ASSERT(script->hasParallelIonScript());
-                if (!invalid.append(script->parallelIonScript()->recompileInfo()))
-                    return false;
-            }
-            pendingInvalidations[i] = NULL;
-        }
-        Invalidate(cx_, invalid);
-        return true;
-    }
-
-    bool warmupForParallelExecution() {
-        AutoEnterWarmup warmup(cx_->runtime);
-        return executeSequentially();
-    }
-#endif // JS_ION
-
-    bool executeSequentially() {
-        uint32_t numSlices = ForkJoinSlices(cx_);
-        RootedValue funVal(TlsPerThreadData.get(), ObjectValue(*fun_));
-        FastInvokeGuard fig(cx_, funVal);
-        for (uint32_t i = 0; i < numSlices; i++) {
-            InvokeArgsGuard &args = fig.args();
-            if (!args.pushed() && !cx_->stack.pushInvokeArgs(cx_, 3, &args))
-                return false;
-            args.setCallee(funVal);
-            args.setThis(UndefinedValue());
-            args[0].setInt32(i);
-            args[1].setInt32(numSlices);
-            args[2].setBoolean(!!cx_->runtime->parallelWarmup);
-            if (!fig.invoke(cx_))
-                return false;
-        }
-        return true;
-    }
-
-    virtual bool parallel(ForkJoinSlice &slice) {
-#ifndef JS_ION
-        JS_NOT_REACHED("Parallel execution without ion");
-        return false;
-#else
-        Spew(SpewOps, "Up");
-
-        // Make a new IonContext for the slice, which is needed if we need to
-        // re-enter the VM.
-        IonContext icx(cx_, NULL);
-
-        JS_ASSERT(pendingInvalidations[slice.sliceId] == NULL);
-
-        JS_ASSERT(fun_->isFunction());
-        RootedFunction callee(TlsPerThreadData.get(), fun_->toFunction());
-        if (!callee->nonLazyScript()->hasParallelIonScript()) {
-            // Sometimes, particularly with GCZeal, the parallel ion
-            // script can be collected between starting the parallel
-            // op and reaching this point.  In that case, we just fail
-            // and fallback.
-            Spew(SpewOps, "Down (Script no longer present)");
-            return false;
-        }
-
-        ParallelIonInvoke<3> fii(cx_, callee, 3);
-
-        fii.args[0] = Int32Value(slice.sliceId);
-        fii.args[1] = Int32Value(slice.numSlices);
-        fii.args[2] = BooleanValue(false);
-
-        bool ok = fii.invoke();
-        JS_ASSERT(ok == !slice.abortedScript);
-        if (!ok) {
-            JSScript *script = slice.abortedScript;
-            Spew(SpewBailouts, "Aborted script: %p (hasParallelIonScript? %d)",
-                 script, script->hasParallelIonScript());
-            JS_ASSERT(script->hasParallelIonScript());
-            pendingInvalidations[slice.sliceId] = script;
-        }
-
-        Spew(SpewOps, "Down");
-
-        return ok;
-#endif // JS_ION
-    }
-
-    inline bool hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script) {
-        for (uint32_t i = 0; i < scripts.length(); i++) {
-            if (scripts[i] == script->parallelIonScript()->recompileInfo())
-                return true;
-        }
-        return false;
-    }
-
-    inline bool hasNoPendingInvalidations() {
-        for (uint32_t i = 0; i < pendingInvalidations.length(); i++) {
-            if (pendingInvalidations[i] != NULL)
-                return false;
-        }
-        return true;
-    }
-};
-
-bool
-js::parallel::Do(JSContext *cx, CallArgs &args)
-{
-    JS_ASSERT(args[0].isObject());
-    JS_ASSERT(args[0].toObject().isFunction());
-
-    RootedObject fun(TlsPerThreadData.get(), &args[0].toObject());
-    ParallelDo op(cx, fun);
-    ExecutionStatus status = op.apply();
-    if (status == ExecutionFatal)
-        return false;
-
-    if (args[1].isObject()) {
-        RootedObject feedback(TlsPerThreadData.get(), &args[1].toObject());
-        if (feedback && feedback->isFunction()) {
-            InvokeArgsGuard feedbackArgs;
-            if (!cx->stack.pushInvokeArgs(cx, 1, &feedbackArgs))
-                return false;
-            feedbackArgs.setCallee(ObjectValue(*feedback));
-            feedbackArgs.setThis(UndefinedValue());
-            if (status == ExecutionParallel)
-                feedbackArgs[0].setInt32(op.bailouts);
-            else
-                feedbackArgs[0] = cx->runtime->positiveInfinityValue;
-            if (!Invoke(cx, feedbackArgs))
-                return false;
-        }
-    }
-
-    return true;
-}
-
deleted file mode 100644
--- a/js/src/vm/ParallelDo.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef ParallelDo_h__
-#define ParallelDo_h__
-
-#include "jsapi.h"
-#include "jscntxt.h"
-#include "jsobj.h"
-#include "ion/Ion.h"
-
-namespace js {
-namespace parallel {
-
-///////////////////////////////////////////////////////////////////////////
-// The main Parallel Execution function.  This is exposed as the
-// intrinsic ParallelDo().
-
-bool Do(JSContext *cx, CallArgs &args);
-
-///////////////////////////////////////////////////////////////////////////
-// Debug Spew
-
-enum ExecutionStatus {
-    // Parallel or seq execution terminated in a fatal way, operation failed
-    ExecutionFatal,
-
-    // Parallel exec failed and so we fell back to sequential
-    ExecutionSequential,
-
-    // Parallel exec was successful after some number of bailouts
-    ExecutionParallel
-};
-
-enum SpewChannel {
-    SpewOps,
-    SpewCompile,
-    SpewBailouts,
-    NumSpewChannels
-};
-
-#if defined(DEBUG) && defined(JS_THREADSAFE) && defined(JS_ION)
-
-bool SpewEnabled(SpewChannel channel);
-void Spew(SpewChannel channel, const char *fmt, ...);
-void SpewBeginOp(JSContext *cx, const char *name);
-void SpewBailout(uint32_t count);
-ExecutionStatus SpewEndOp(ExecutionStatus status);
-void SpewBeginCompile(HandleScript script);
-ion::MethodStatus SpewEndCompile(ion::MethodStatus status);
-void SpewMIR(ion::MDefinition *mir, const char *fmt, ...);
-void SpewBailoutIR(uint32_t bblockId, uint32_t lirId,
-                   const char *lir, const char *mir, JSScript *script, jsbytecode *pc);
-
-#else
-
-static inline bool SpewEnabled(SpewChannel channel) { return false; }
-static inline void Spew(SpewChannel channel, const char *fmt, ...) { }
-static inline void SpewBeginOp(JSContext *cx, const char *name) { }
-static inline void SpewBailout(uint32_t count) {}
-static inline ExecutionStatus SpewEndOp(ExecutionStatus status) { return status; }
-static inline void SpewBeginCompile(HandleScript script) { }
-#ifdef JS_ION
-static inline ion::MethodStatus SpewEndCompile(ion::MethodStatus status) { return status; }
-static inline void SpewMIR(ion::MDefinition *mir, const char *fmt, ...) { }
-#endif
-static inline void SpewBailoutIR(uint32_t bblockId, uint32_t lirId,
-                                 const char *lir, const char *mir,
-                                 JSScript *script, jsbytecode *pc) { }
-
-#endif // DEBUG && JS_THREADSAFE && JS_ION
-
-} // namespace parallel
-} // namespace js
-
-#endif
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -7,18 +7,18 @@
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsinterp.h"
 #include "jsobj.h"
 
 #include "builtin/Intl.h"
 #include "builtin/ParallelArray.h"
 #include "gc/Marking.h"
+
 #include "vm/ForkJoin.h"
-#include "vm/ParallelDo.h"
 #include "vm/ThreadPool.h"
 
 #include "jsfuninlines.h"
 #include "jstypedarrayinlines.h"
 
 #include "ion/BaselineJIT.h"
 
 #include "vm/BooleanObject-inl.h"
@@ -233,54 +233,33 @@ js::intrinsic_Dump(JSContext *cx, unsign
     RootedValue val(cx, args[0]);
     js_DumpValue(val);
     args.rval().setUndefined();
     return true;
 }
 #endif
 
 /*
- * ParallelDo(func, feedback): Invokes |func| many times in parallel.
- *
- * Executed based on the fork join pool described in vm/ForkJoin.h.
- * If func() has not been compiled for parallel execution, it will
- * first be invoked various times sequentially as a warmup phase,
- * which is used to gather TI information and to determine which
- * functions func() will invoke.
- *
- * func() should expect the following arguments:
- *
- *     func(id, n, warmup, args...)
+ * ForkJoin(func, feedback): Invokes |func| many times in parallel.
  *
- * Here, |id| is the slice id. |n| is the total number of slices;
- * |warmup| is true if this is a warmup or recovery phase.
- * Typically, if |warmup| is true, you will want to do less work.
- *
- * The |feedback| argument is optional.  If provided, it should be a
- * closure.  This closure will be invoked with a double argument
- * representing the number of bailouts that occurred before a
- * successful parallel execution.  If the number is infinity, then
- * parallel execution was abandoned and |func| was simply invoked
- * sequentially.
- *
- * See ParallelArray.js for examples.
+ * See ForkJoin.cpp for details and ParallelArray.js for examples.
  */
 static JSBool
-intrinsic_ParallelDo(JSContext *cx, unsigned argc, Value *vp)
+intrinsic_ForkJoin(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    return parallel::Do(cx, args);
+    return ForkJoin(cx, args);
 }
 
 /*
- * ParallelSlices(): Returns the number of parallel slices that will
- * be created by ParallelDo().
+ * ForkJoinSlices(): Returns the number of parallel slices that will
+ * be created by ForkJoin().
  */
 static JSBool
-intrinsic_ParallelSlices(JSContext *cx, unsigned argc, Value *vp)
+intrinsic_ForkJoinSlices(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setInt32(ForkJoinSlices(cx));
     return true;
 }
 
 /*
  * NewParallelArray(init, ...args): Creates a new parallel array using
@@ -473,18 +452,18 @@ const JSFunctionSpec intrinsic_functions
     JS_FN("ThrowError",           intrinsic_ThrowError,           4,0),
     JS_FN("AssertionFailed",      intrinsic_AssertionFailed,      1,0),
     JS_FN("SetScriptHints",       intrinsic_SetScriptHints,       2,0),
     JS_FN("MakeConstructible",    intrinsic_MakeConstructible,    1,0),
     JS_FN("MakeWrappable",        intrinsic_MakeWrappable,        1,0),
     JS_FN("DecompileArg",         intrinsic_DecompileArg,         2,0),
     JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
 
-    JS_FN("ParallelDo",           intrinsic_ParallelDo,           2,0),
-    JS_FN("ParallelSlices",       intrinsic_ParallelSlices,       0,0),
+    JS_FN("ForkJoin",             intrinsic_ForkJoin,             2,0),
+    JS_FN("ForkJoinSlices",       intrinsic_ForkJoinSlices,       0,0),
     JS_FN("NewParallelArray",     intrinsic_NewParallelArray,     3,0),
     JS_FN("NewDenseArray",        intrinsic_NewDenseArray,        1,0),
     JS_FN("UnsafeSetElement",     intrinsic_UnsafeSetElement,     3,0),
     JS_FN("ShouldForceSequential", intrinsic_ShouldForceSequential, 0,0),
     JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0),
 
     // See builtin/Intl.h for descriptions of the intl_* functions.
     JS_FN("intl_availableCalendars", intl_availableCalendars, 1,0),