Bug 825714: Refactor JS allocation routines to enable allocation on parallel
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Thu, 24 Jan 2013 21:12:44 -0800
changeset 129727 ace1e79d65d90f12ec5644eee5ceecce25c90649
parent 129726 342c2b3494021b9e41b921b1544de8ecb4e195b9
child 129728 0e518197a82381eef06b374b1bcb4e2c9ae3602d
push idunknown
push userunknown
push dateunknown
bugs825714
milestone21.0a1
Bug 825714: Refactor JS allocation routines to enable allocation on parallel threads, and move various ion fields into per-thread-data. r=billm More detailed: - A new routine JSObject::parExtendDenseArray() that permits parallel code to allocate a dense array on its own Allocator. - Create an allocation path in the GC for loading a fresh arena as needed (ArenaLists::parallelAllocate()). - Ensure that if GC is triggered during parallel execution, parallel execution is aborted and the request is deferred until parallel execution terminates. - Updates to the ForkJoin/ThreadPool so that they create their own Allocators as well as other misc API changes. - Moves some of the ion-related fields (e.g., |ionTop|) into perThreadData. - Remove out-of-date malloc tracking fields.
js/src/gc/Verifier.cpp
js/src/ion/Bailouts.cpp
js/src/ion/CodeGenerator.cpp
js/src/ion/Ion.cpp
js/src/ion/IonFrames-inl.h
js/src/ion/IonFrames.cpp
js/src/ion/IonMacroAssembler.cpp
js/src/ion/IonMacroAssembler.h
js/src/ion/arm/MacroAssembler-arm.cpp
js/src/ion/arm/Trampoline-arm.cpp
js/src/ion/x64/MacroAssembler-x64.h
js/src/ion/x86/MacroAssembler-x86.h
js/src/jsapi-tests/testFindSCCs.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jscompartmentinlines.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jsinterpinlines.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jspubtd.h
js/src/jsxml.cpp
js/src/methodjit/BaseAssembler.h
js/src/methodjit/MonoIC.cpp
js/src/shell/js.cpp
js/src/vm/CharacterEncoding.cpp
js/src/vm/CommonPropertyNames.h
js/src/vm/ForkJoin-inl.h
js/src/vm/ForkJoin.cpp
js/src/vm/ForkJoin.h
js/src/vm/Stack.cpp
js/src/vm/String-inl.h
js/src/vm/ThreadPool.cpp
js/src/vm/ThreadPool.h
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -504,17 +504,17 @@ gc::StartVerifyPreBarriers(JSRuntime *rt
     }
 
     rt->gcVerifyPreData = trc;
     rt->gcIncrementalState = MARK;
     rt->gcMarker.start();
     for (CompartmentsIter c(rt); !c.done(); c.next()) {
         PurgeJITCaches(c);
         c->setNeedsBarrier(true, JSCompartment::UpdateIon);
-        c->arenas.purge();
+        c->allocator.arenas.purge();
     }
 
     return;
 
 oom:
     rt->gcIncrementalState = NO_INCREMENTAL;
     trc->~VerifyPreTracer();
     js_free(trc);
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -76,17 +76,17 @@ IonBailoutIterator::dump() const
 
 static UnrootedScript
 GetBailedJSScript(JSContext *cx)
 {
     AutoAssertNoGC nogc;
 
     // Just after the frame conversion, we can safely interpret the ionTop as JS
     // frame because it targets the bailed JS frame converted to an exit frame.
-    IonJSFrameLayout *frame = reinterpret_cast<IonJSFrameLayout*>(cx->runtime->ionTop);
+    IonJSFrameLayout *frame = reinterpret_cast<IonJSFrameLayout*>(cx->mainThread().ionTop);
     switch (GetCalleeTokenTag(frame->calleeToken())) {
       case CalleeToken_Function: {
         JSFunction *fun = CalleeTokenToFunction(frame->calleeToken());
         return fun->nonLazyScript();
       }
       case CalleeToken_Script:
         return CalleeTokenToScript(frame->calleeToken());
       default:
@@ -355,17 +355,17 @@ EnsureExitFrame(IonCommonFrameLayout *fr
 }
 
 uint32_t
 ion::Bailout(BailoutStack *sp)
 {
     AutoAssertNoGC nogc;
     JSContext *cx = GetIonContext()->cx;
     // We don't have an exit frame.
-    cx->runtime->ionTop = NULL;
+    cx->mainThread().ionTop = NULL;
     IonActivationIterator ionActivations(cx);
     IonBailoutIterator iter(ionActivations, sp);
     IonActivation *activation = ionActivations.activation();
 
     // IonCompartment *ioncompartment = cx->compartment->ionCompartment();
     // IonActivation *activation = cx->runtime->ionActivation;
     // FrameRecovery in = FrameRecoveryFromBailout(ioncompartment, sp);
 
@@ -381,17 +381,17 @@ uint32_t
 ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut)
 {
     AutoAssertNoGC nogc;
     sp->checkInvariants();
 
     JSContext *cx = GetIonContext()->cx;
 
     // We don't have an exit frame.
-    cx->runtime->ionTop = NULL;
+    cx->mainThread().ionTop = NULL;
     IonActivationIterator ionActivations(cx);
     IonBailoutIterator iter(ionActivations, sp);
     IonActivation *activation = ionActivations.activation();
 
     IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
 
     // Note: the frame size must be computed before we return from this function.
     *frameSizeOut = iter.topFrameSize();
@@ -453,17 +453,17 @@ ReflowArgTypes(JSContext *cx)
     for (unsigned i = 0; i < nargs; ++i)
         types::TypeScript::SetArgument(cx, script, i, fp->unaliasedFormal(i, DONT_CHECK_ALIASING));
 }
 
 uint32_t
 ion::ReflowTypeInfo(uint32_t bailoutResult)
 {
     JSContext *cx = GetIonContext()->cx;
-    IonActivation *activation = cx->runtime->ionActivation;
+    IonActivation *activation = cx->mainThread().ionActivation;
 
     IonSpew(IonSpew_Bailouts, "reflowing type info");
 
     if (bailoutResult == BAILOUT_RETURN_ARGUMENT_CHECK) {
         IonSpew(IonSpew_Bailouts, "reflowing type info at argument-checked entry");
         ReflowArgTypes(cx);
         return true;
     }
@@ -579,17 +579,17 @@ ion::CachedShapeGuardFailure()
 
     return Invalidate(cx, script);
 }
 
 uint32_t
 ion::ThunkToInterpreter(Value *vp)
 {
     JSContext *cx = GetIonContext()->cx;
-    IonActivation *activation = cx->runtime->ionActivation;
+    IonActivation *activation = cx->mainThread().ionActivation;
     BailoutClosure *br = activation->takeBailout();
     InterpMode resumeMode = JSINTERP_BAILOUT;
 
     if (!EnsureHasScopeObjects(cx, cx->fp()))
         resumeMode = JSINTERP_RETHROW;
 
     // By default we set the forbidOsr flag on the ion script, but if a GC
     // happens just after we re-enter the interpreter, the ion script get
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1483,17 +1483,17 @@ CodeGenerator::visitCheckOverRecursed(LC
     // Ion may legally place frames very close to the limit. Calling additional
     // C functions may then violate the limit without any checking.
 
     JSRuntime *rt = gen->compartment->rt;
     Register limitReg = ToRegister(lir->limitTemp());
 
     // Since Ion frames exist on the C stack, the stack limit may be
     // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota().
-    uintptr_t *limitAddr = &rt->ionStackLimit;
+    uintptr_t *limitAddr = &rt->mainThread.ionStackLimit;
     masm.loadPtr(AbsoluteAddress(limitAddr), limitReg);
 
     CheckOverRecursedFailure *ool = new CheckOverRecursedFailure(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
     // Conditional forward (unlikely) branch to failure.
     masm.branchPtr(Assembler::BelowOrEqual, StackPointer, limitReg, ool->entry());
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -13,19 +13,21 @@
 #include "LIR.h"
 #include "AliasAnalysis.h"
 #include "LICM.h"
 #include "ValueNumbering.h"
 #include "EdgeCaseAnalysis.h"
 #include "RangeAnalysis.h"
 #include "LinearScan.h"
 #include "jscompartment.h"
-#include "jsworkers.h"
+#include "vm/ThreadPool.h"
+#include "vm/ForkJoin.h"
 #include "IonCompartment.h"
 #include "CodeGenerator.h"
+#include "jsworkers.h"
 #include "BacktrackingAllocator.h"
 #include "StupidAllocator.h"
 #include "UnreachableCodeElimination.h"
 
 #if defined(JS_CPU_X86)
 # include "x86/Lowering-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/Lowering-x64.h"
@@ -114,16 +116,20 @@ IonContext::~IonContext()
 bool
 ion::InitializeIon()
 {
 #ifdef JS_THREADSAFE
     if (!IonTLSInitialized) {
         PRStatus status = PR_NewThreadPrivateIndex(&IonTLSIndex, NULL);
         if (status != PR_SUCCESS)
             return false;
+
+        if (!ForkJoinSlice::Initialize())
+            return false;
+
         IonTLSInitialized = true;
     }
 #endif
     CheckLogging();
     return true;
 }
 
 IonRuntime::IonRuntime()
@@ -215,16 +221,18 @@ bool
 IonCompartment::initialize(JSContext *cx)
 {
     return true;
 }
 
 void
 ion::FinishOffThreadBuilder(IonBuilder *builder)
 {
+    JS_ASSERT(builder->info().executionMode() == SequentialExecution);
+
     // Clean up if compilation did not succeed.
     if (builder->script()->isIonCompilingOffThread()) {
         types::TypeCompartment &types = builder->script()->compartment()->types;
         builder->recompileInfo.compilerOutput(types)->invalidate();
         builder->script()->ion = NULL;
     }
 
     // The builder is allocated into its LifoAlloc, so destroying that will
@@ -287,40 +295,40 @@ IonCompartment::getVMWrapper(const VMFun
     JS_ASSERT(p);
 
     return p->value;
 }
 
 IonActivation::IonActivation(JSContext *cx, StackFrame *fp)
   : cx_(cx),
     compartment_(cx->compartment),
-    prev_(cx->runtime->ionActivation),
+    prev_(cx->mainThread().ionActivation),
     entryfp_(fp),
     bailout_(NULL),
-    prevIonTop_(cx->runtime->ionTop),
-    prevIonJSContext_(cx->runtime->ionJSContext),
+    prevIonTop_(cx->mainThread().ionTop),
+    prevIonJSContext_(cx->mainThread().ionJSContext),
     prevpc_(NULL)
 {
     if (fp)
         fp->setRunningInIon();
-    cx->runtime->ionJSContext = cx;
-    cx->runtime->ionActivation = this;
-    cx->runtime->ionStackLimit = cx->runtime->nativeStackLimit;
+    cx->mainThread().ionJSContext = cx;
+    cx->mainThread().ionActivation = this;
+    cx->mainThread().ionStackLimit = cx->mainThread().nativeStackLimit;
 }
 
 IonActivation::~IonActivation()
 {
-    JS_ASSERT(cx_->runtime->ionActivation == this);
+    JS_ASSERT(cx_->mainThread().ionActivation == this);
     JS_ASSERT(!bailout_);
 
     if (entryfp_)
         entryfp_->clearRunningInIon();
-    cx_->runtime->ionActivation = prev();
-    cx_->runtime->ionTop = prevIonTop_;
-    cx_->runtime->ionJSContext = prevIonJSContext_;
+    cx_->mainThread().ionActivation = prev();
+    cx_->mainThread().ionTop = prevIonTop_;
+    cx_->mainThread().ionJSContext = prevIonJSContext_;
 }
 
 IonCode *
 IonCode::New(JSContext *cx, uint8_t *code, uint32_t bufferSize, JSC::ExecutablePool *pool)
 {
     AssertCanGC();
 
     IonCode *codeObj = gc::NewGCThing<IonCode, CanGC>(cx, gc::FINALIZE_IONCODE, sizeof(IonCode));
--- a/js/src/ion/IonFrames-inl.h
+++ b/js/src/ion/IonFrames-inl.h
@@ -80,17 +80,17 @@ IonFrameIterator::frameSize() const
     return frameSize_;
 }
 
 // Returns the JSScript associated with the topmost Ion frame.
 inline UnrootedScript
 GetTopIonJSScript(JSContext *cx, const SafepointIndex **safepointIndexOut, void **returnAddrOut)
 {
     AutoAssertNoGC nogc;
-    IonFrameIterator iter(cx->runtime->ionTop);
+    IonFrameIterator iter(cx->mainThread().ionTop);
     JS_ASSERT(iter.type() == IonFrame_Exit);
     ++iter;
 
     // If needed, grab the safepoint index.
     if (safepointIndexOut)
         *safepointIndexOut = iter.safepoint();
 
     JS_ASSERT(iter.returnAddressToFp() != NULL);
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -304,19 +304,19 @@ ion::HandleException(ResumeFromException
 {
     AssertCanGC();
     JSContext *cx = GetIonContext()->cx;
 
     IonSpew(IonSpew_Invalidate, "handling exception");
 
     // Immediately remove any bailout frame guard that might be left over from
     // an error in between ConvertFrames and ThunkToInterpreter.
-    js_delete(cx->runtime->ionActivation->maybeTakeBailout());
+    js_delete(cx->mainThread().ionActivation->maybeTakeBailout());
 
-    IonFrameIterator iter(cx->runtime->ionTop);
+    IonFrameIterator iter(cx->mainThread().ionTop);
     while (!iter.isEntry()) {
         if (iter.isScripted()) {
             // Search each inlined frame for live iterator objects, and close
             // them.
             InlineFrameIterator frames(cx, &iter);
             for (;;) {
                 CloseLiveIterators(cx, frames);
 
@@ -354,25 +354,25 @@ IonActivationIterator::settle()
 {
     while (activation_ && activation_->empty()) {
         top_ = activation_->prevIonTop();
         activation_ = activation_->prev();
     }
 }
 
 IonActivationIterator::IonActivationIterator(JSContext *cx)
-  : top_(cx->runtime->ionTop),
-    activation_(cx->runtime->ionActivation)
+  : top_(cx->mainThread().ionTop),
+    activation_(cx->mainThread().ionActivation)
 {
     settle();
 }
 
 IonActivationIterator::IonActivationIterator(JSRuntime *rt)
-  : top_(rt->ionTop),
-    activation_(rt->ionActivation)
+  : top_(rt->mainThread.ionTop),
+    activation_(rt->mainThread.ionActivation)
 {
     settle();
 }
 
 IonActivationIterator &
 IonActivationIterator::operator++()
 {
     JS_ASSERT(activation_);
@@ -670,17 +670,17 @@ void
 ion::GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes)
 {
     JS_ASSERT(cx->fp()->beginsIonActivation());
     IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame.");
 
     JSRuntime *rt = cx->runtime;
 
     // Recover the return address.
-    IonFrameIterator it(rt->ionTop);
+    IonFrameIterator it(rt->mainThread.ionTop);
     uint8_t *retAddr = it.returnAddress();
     uint32_t hash = PcScriptCache::Hash(retAddr);
     JS_ASSERT(retAddr != NULL);
 
     // Lazily initialize the cache. The allocation may safely fail and will not GC.
     if (JS_UNLIKELY(rt->ionPcScriptCache == NULL)) {
         rt->ionPcScriptCache = (PcScriptCache *)js_malloc(sizeof(struct PcScriptCache));
         if (rt->ionPcScriptCache)
--- a/js/src/ion/IonMacroAssembler.cpp
+++ b/js/src/ion/IonMacroAssembler.cpp
@@ -5,16 +5,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsinfer.h"
 #include "jsinferinlines.h"
 #include "IonMacroAssembler.h"
 #include "gc/Root.h"
 #include "Bailouts.h"
+#include "vm/ForkJoin.h"
 
 using namespace js;
 using namespace js::ion;
 
 template <typename T> void
 MacroAssembler::guardTypeSet(const T &address, const types::TypeSet *types,
                              Register scratch, Label *mismatched)
 {
@@ -316,17 +317,17 @@ MacroAssembler::newGCThing(const Registe
     branch32(Assembler::NotEqual, result, Imm32(0), fail);
 #endif
 
     // Inline FreeSpan::allocate.
     // There is always exactly one FreeSpan per allocKind per JSCompartment.
     // If a FreeSpan is replaced, its members are updated in the freeLists table,
     // which the code below always re-reads.
     gc::FreeSpan *list = const_cast<gc::FreeSpan *>
-                         (compartment->arenas.getFreeList(allocKind));
+                         (compartment->allocator.arenas.getFreeList(allocKind));
     loadPtr(AbsoluteAddress(&list->first), result);
     branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(&list->last), result, fail);
 
     addPtr(Imm32(thingSize), result);
     storePtr(result, AbsoluteAddress(&list->first));
     subPtr(Imm32(thingSize), result);
 }
 
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -154,21 +154,21 @@ class MacroAssembler : public MacroAssem
 
     void loadStringLength(Register str, Register dest) {
         loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), dest);
         rshiftPtr(Imm32(JSString::LENGTH_SHIFT), dest);
     }
 
     void loadJSContext(const Register &dest) {
         movePtr(ImmWord(GetIonContext()->compartment->rt), dest);
-        loadPtr(Address(dest, offsetof(JSRuntime, ionJSContext)), dest);
+        loadPtr(Address(dest, offsetof(JSRuntime, mainThread.ionJSContext)), dest);
     }
     void loadIonActivation(const Register &dest) {
         movePtr(ImmWord(GetIonContext()->compartment->rt), dest);
-        loadPtr(Address(dest, offsetof(JSRuntime, ionActivation)), dest);
+        loadPtr(Address(dest, offsetof(JSRuntime, mainThread.ionActivation)), dest);
     }
 
     template<typename T>
     void loadTypedOrValue(const T &src, TypedOrValueRegister dest) {
         if (dest.hasValue())
             loadValue(src, dest.valueReg());
         else
             loadUnboxedValue(src, dest.type(), dest.typedReg());
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -2625,17 +2625,17 @@ MacroAssemblerARMCompat::storeTypeTag(Im
     ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET), base);
     ma_mov(tag, ScratchRegister);
     ma_str(ScratchRegister, DTRAddr(base, DtrRegImmShift(index, LSL, shift)));
     ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET), base);
 }
 
 void
 MacroAssemblerARMCompat::linkExitFrame() {
-    uint8_t *dest = ((uint8_t*)GetIonContext()->compartment->rt) + offsetof(JSRuntime, ionTop);
+    uint8_t *dest = ((uint8_t*)GetIonContext()->compartment->rt) + offsetof(JSRuntime, mainThread.ionTop);
     movePtr(ImmWord(dest), ScratchRegister);
     ma_str(StackPointer, Operand(ScratchRegister, 0));
 }
 
 // ARM says that all reads of pc will return 8 higher than the
 // address of the currently executing instruction.  This means we are
 // correctly storing the address of the instruction after the call
 // in the register.
--- a/js/src/ion/arm/Trampoline-arm.cpp
+++ b/js/src/ion/arm/Trampoline-arm.cpp
@@ -9,16 +9,18 @@
 #include "assembler/assembler/MacroAssembler.h"
 #include "ion/IonCompartment.h"
 #include "ion/IonLinker.h"
 #include "ion/IonFrames.h"
 #include "ion/IonSpewer.h"
 #include "ion/Bailouts.h"
 #include "ion/VMFunctions.h"
 
+#include "jscntxtinlines.h"
+
 using namespace js;
 using namespace js::ion;
 
 static void
 GenerateReturn(MacroAssembler &masm, int returnCode)
 {
     // Restore non-volatile registers
     masm.ma_mov(Imm32(returnCode), r0);
--- a/js/src/ion/x64/MacroAssembler-x64.h
+++ b/js/src/ion/x64/MacroAssembler-x64.h
@@ -884,17 +884,17 @@ class MacroAssemblerX64 : public MacroAs
         shlq(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
         orq(Imm32(type), frameSizeReg);
     }
 
     // Save an exit frame (which must be aligned to the stack pointer) to
     // ThreadData::ionTop.
     void linkExitFrame() {
         mov(ImmWord(GetIonContext()->compartment->rt), ScratchReg);
-        mov(StackPointer, Operand(ScratchReg, offsetof(JSRuntime, ionTop)));
+        mov(StackPointer, Operand(ScratchReg, offsetof(JSRuntime, mainThread.ionTop)));
     }
 
     void callWithExitFrame(IonCode *target, Register dynStack) {
         addPtr(Imm32(framePushed()), dynStack);
         makeFrameDescriptor(dynStack, IonFrame_OptimizedJS);
         Push(dynStack);
         call(target);
     }
--- a/js/src/ion/x86/MacroAssembler-x86.h
+++ b/js/src/ion/x86/MacroAssembler-x86.h
@@ -753,17 +753,17 @@ class MacroAssemblerX86 : public MacroAs
         shll(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
         orl(Imm32(type), frameSizeReg);
     }
 
     // Save an exit frame (which must be aligned to the stack pointer) to
     // ThreadData::ionTop.
     void linkExitFrame() {
         JSCompartment *compartment = GetIonContext()->compartment;
-        movl(StackPointer, Operand(&compartment->rt->ionTop));
+        movl(StackPointer, Operand(&compartment->rt->mainThread.ionTop));
     }
 
     void callWithExitFrame(IonCode *target, Register dynStack) {
         addPtr(Imm32(framePushed()), dynStack);
         makeFrameDescriptor(dynStack, IonFrame_OptimizedJS);
         Push(dynStack);
         call(target);
     }
--- a/js/src/jsapi-tests/testFindSCCs.cpp
+++ b/js/src/jsapi-tests/testFindSCCs.cpp
@@ -150,17 +150,17 @@ void setup(unsigned count)
 
 void edge(unsigned src_index, unsigned dest_index)
 {
     Vertex[src_index].hasEdge[dest_index] = true;
 }
 
 void run()
 {
-    finder = new ComponentFinder<TestNode>(rt->nativeStackLimit);
+    finder = new ComponentFinder<TestNode>(rt->mainThread.nativeStackLimit);
     for (unsigned i = 0; i < vertex_count; ++i)
         finder->addNode(&Vertex[i]);
     resultsList = finder->getResultsList();
 }
 
 bool group(int vertex, ...)
 {
     TestNode *v = resultsList;
@@ -241,17 +241,17 @@ BEGIN_TEST(testFindSCCsStackLimit)
      */
     const unsigned max = 1000000;
     const unsigned initial = 10;
 
     TestNode2 *vertices = new TestNode2[max]();
     for (unsigned i = initial; i < (max - 10); ++i)
         vertices[i].edge = &vertices[i + 1];
 
-    ComponentFinder<TestNode2> finder(rt->nativeStackLimit);
+    ComponentFinder<TestNode2> finder(rt->mainThread.nativeStackLimit);
     for (unsigned i = 0; i < max; ++i)
         finder.addNode(&vertices[i]);
 
     TestNode2 *r = finder.getResultsList();
     CHECK(r);
     TestNode2 *v = r;
 
     unsigned count = 0;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -715,22 +715,27 @@ JS_FRIEND_API(void) JS::EnterAssertNoGCS
 JS_FRIEND_API(void) JS::LeaveAssertNoGCScope() {}
 JS_FRIEND_API(bool) JS::InNoGCScope() { return false; }
 JS_FRIEND_API(bool) JS::isGCEnabled() { return true; }
 JS_FRIEND_API(bool) JS::NeedRelaxedRootChecks() { return false; }
 #endif
 
 static const JSSecurityCallbacks NullSecurityCallbacks = { };
 
-PerThreadData::PerThreadData(JSRuntime *runtime)
-  : runtime_(runtime),
+js::PerThreadData::PerThreadData(JSRuntime *runtime)
+  : PerThreadDataFriendFields(),
+    runtime_(runtime),
 #ifdef DEBUG
     gcRelaxRootChecks(false),
     gcAssertNoGCDepth(0),
 #endif
+    ionTop(NULL),
+    ionJSContext(NULL),
+    ionStackLimit(0),
+    ionActivation(NULL),
     suppressGC(0)
 {}
 
 JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
   : mainThread(this),
     atomsCompartment(NULL),
 #ifdef JS_THREADSAFE
     ownerThread_(NULL),
@@ -873,20 +878,16 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
     wrapObjectCallback(TransparentObjectWrapper),
     sameCompartmentWrapObjectCallback(NULL),
     preWrapObjectCallback(NULL),
     preserveWrapperCallback(NULL),
 #ifdef DEBUG
     noGCOrAllocationCheck(0),
 #endif
     jitHardening(false),
-    ionTop(NULL),
-    ionJSContext(NULL),
-    ionStackLimit(0),
-    ionActivation(NULL),
     ionPcScriptCache(NULL),
     threadPool(this),
     ctypesActivityCallback(NULL),
     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
     useHelperThreads_(useHelperThreads),
     requestedHelperThreadCount(-1),
     rngNonce(0)
 {
@@ -928,17 +929,17 @@ JSRuntime::init(uint32_t maxbytes)
         !atomsCompartment->init(NULL) ||
         !compartments.append(atomsCompartment))
     {
         js_delete(atomsCompartment);
         return false;
     }
 
     atomsCompartment->isSystemCompartment = true;
-    atomsCompartment->setGCLastBytes(8192, 8192, GC_NORMAL);
+    atomsCompartment->setGCLastBytes(8192, GC_NORMAL);
 
     if (!InitAtoms(this))
         return false;
 
     if (!InitRuntimeNumberState(this))
         return false;
 
     dtoaState = js_NewDtoaState();
@@ -1050,19 +1051,19 @@ JSRuntime::clearOwnerThread()
 {
     assertValidThread();
     JS_ASSERT(requestDepth == 0);
     JS_ASSERT(js_NewRuntimeWasCalled);
     ownerThread_ = (void *)0xc1ea12;  /* "clear" */
     js::TlsPerThreadData.set(NULL);
     nativeStackBase = 0;
 #if JS_STACK_GROWTH_DIRECTION > 0
-    nativeStackLimit = UINTPTR_MAX;
+    mainThread.nativeStackLimit = UINTPTR_MAX;
 #else
-    nativeStackLimit = 0;
+    mainThread.nativeStackLimit = 0;
 #endif
 }
 
 JS_FRIEND_API(void)
 JSRuntime::abortIfWrongThread() const
 {
     if (ownerThread_ != PR_GetCurrentThread())
         MOZ_CRASH();
@@ -2258,28 +2259,16 @@ JS_ComputeThis(JSContext *cx, jsval *vp)
     AssertHeapIsIdle(cx);
     assertSameCompartment(cx, JSValueArray(vp, 2));
     CallReceiver call = CallReceiverFromVp(vp);
     if (!BoxNonStrictThis(cx, call))
         return JSVAL_NULL;
     return call.thisv();
 }
 
-JS_PUBLIC_API(void)
-JS_MallocInCompartment(JSCompartment *comp, size_t nbytes)
-{
-    comp->mallocInCompartment(nbytes);
-}
-
-JS_PUBLIC_API(void)
-JS_FreeInCompartment(JSCompartment *comp, size_t nbytes)
-{
-    comp->freeInCompartment(nbytes);
-}
-
 JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     return cx->malloc_(nbytes);
 }
 
@@ -2307,17 +2296,17 @@ JS_PUBLIC_API(JSFreeOp *)
 JS_GetDefaultFreeOp(JSRuntime *rt)
 {
     return rt->defaultFreeOp();
 }
 
 JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext *cx, size_t nbytes)
 {
-    return cx->runtime->updateMallocCounter(cx, nbytes);
+    return cx->runtime->updateMallocCounter(cx->compartment, nbytes);
 }
 
 JS_PUBLIC_API(char *)
 JS_strdup(JSContext *cx, const char *s)
 {
     AssertHeapIsIdle(cx);
     size_t n = strlen(s) + 1;
     void *p = cx->malloc_(n);
@@ -3095,27 +3084,27 @@ JS_PUBLIC_API(void)
 JS_SetNativeStackQuota(JSRuntime *rt, size_t stackSize)
 {
     rt->nativeStackQuota = stackSize;
     if (!rt->nativeStackBase)
         return;
 
 #if JS_STACK_GROWTH_DIRECTION > 0
     if (stackSize == 0) {
-        rt->nativeStackLimit = UINTPTR_MAX;
+        rt->mainThread.nativeStackLimit = UINTPTR_MAX;
     } else {
         JS_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize);
-        rt->nativeStackLimit = rt->nativeStackBase + stackSize - 1;
+        rt->mainThread.nativeStackLimit = rt->nativeStackBase + stackSize - 1;
     }
 #else
     if (stackSize == 0) {
-        rt->nativeStackLimit = 0;
+        rt->mainThread.nativeStackLimit = 0;
     } else {
         JS_ASSERT(rt->nativeStackBase >= stackSize);
-        rt->nativeStackLimit = rt->nativeStackBase - (stackSize - 1);
+        rt->mainThread.nativeStackLimit = rt->nativeStackBase - (stackSize - 1);
     }
 #endif
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(int)
 JS_IdArrayLength(JSContext *cx, JSIdArray *ida)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3533,22 +3533,16 @@ JS_THIS(JSContext *cx, jsval *vp)
  * or vice versa.  Either use the provided this value with this macro, or
  * compute the boxed this value using those.
  *
  * N.B. constructors must not use JS_THIS_VALUE, as no 'this' object has been
  * created.
  */
 #define JS_THIS_VALUE(cx,vp)    ((vp)[1])
 
-extern JS_PUBLIC_API(void)
-JS_MallocInCompartment(JSCompartment *comp, size_t nbytes);
-
-extern JS_PUBLIC_API(void)
-JS_FreeInCompartment(JSCompartment *comp, size_t nbytes);
-
 extern JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes);
 
 extern JS_PUBLIC_API(void *)
 JS_realloc(JSContext *cx, void *p, size_t nbytes);
 
 /*
  * A wrapper for js_free(p) that may delay js_free(p) invocation as a
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1410,16 +1410,18 @@ js::array_sort(JSContext *cx, unsigned a
 
                 /* Order vec[n:2n-1] using strElements.index */
                 for (size_t i = 0; i < n; i ++)
                     vec[n + i] = vec[strElements[i].elementIndex];
 
                 result = vec.begin() + n;
             }
         } else {
+            /* array.sort() cannot currently be used from parallel code */
+            JS_ASSERT(!ForkJoinSlice::InParallelSection());
             FastInvokeGuard fig(cx, fval);
             if (!MergeSort(vec.begin(), n, vec.begin() + n,
                            SortComparatorFunction(cx, fval, fig))) {
                 return false;
             }
         }
 
         if (!InitArrayElements(cx, obj, 0, uint32_t(n), result, DontUpdateTypes))
@@ -2198,16 +2200,17 @@ array_map(JSContext *cx, unsigned argc, 
         return false;
     arr->setType(newtype);
 
     /* Step 7. */
     uint32_t k = 0;
 
     /* Step 8. */
     RootedValue kValue(cx);
+    JS_ASSERT(!ForkJoinSlice::InParallelSection());
     FastInvokeGuard fig(cx, ObjectValue(*callable));
     InvokeArgsGuard &ag = fig.args();
     while (k < len) {
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;
 
         /* Step a, b, and c.i. */
         JSBool kNotPresent;
@@ -2278,16 +2281,17 @@ array_filter(JSContext *cx, unsigned arg
 
     /* Step 7. */
     uint32_t k = 0;
 
     /* Step 8. */
     uint32_t to = 0;
 
     /* Step 9. */
+    JS_ASSERT(!ForkJoinSlice::InParallelSection());
     FastInvokeGuard fig(cx, ObjectValue(*callable));
     InvokeArgsGuard &ag = fig.args();
     RootedValue kValue(cx);
     while (k < len) {
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;
 
         /* Step a, b, and c.i. */
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -168,17 +168,17 @@ void
 JSRuntime::triggerOperationCallback()
 {
     /*
      * Invalidate ionTop to trigger its over-recursion check. Note this must be
      * set before interrupt, to avoid racing with js_InvokeOperationCallback,
      * into a weird state where interrupt is stuck at 0 but ionStackLimit is
      * MAXADDR.
      */
-    ionStackLimit = -1;
+    mainThread.ionStackLimit = -1;
 
     /*
      * Use JS_ATOMIC_SET in the hope that it ensures the write will become
      * immediately visible to other processors polling the flag.
      */
     JS_ATOMIC_SET(&interrupt, 1);
 }
 
@@ -462,17 +462,17 @@ ReportError(JSContext *cx, const char *m
         js_ReportErrorAgain(cx, message, reportp);
     } else if (JSDebugErrorHook hook = cx->runtime->debugHooks.debugErrorHook) {
         /*
          * If we've already chewed up all the C stack, don't call into the
          * error reporter since this may trigger an infinite recursion where
          * the reporter triggers an over-recursion.
          */
         int stackDummy;
-        if (!JS_CHECK_STACK_SIZE(cx->runtime->nativeStackLimit, &stackDummy))
+        if (!JS_CHECK_STACK_SIZE(cx->mainThread().nativeStackLimit, &stackDummy))
             return;
 
         if (cx->errorReporter)
             hook(cx, message, reportp, cx->runtime->debugHooks.debugErrorHookData);
     }
 }
 
 /*
@@ -1338,26 +1338,26 @@ JSRuntime::setGCMaxMallocBytes(size_t va
      * mean that value.
      */
     gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
     for (CompartmentsIter c(this); !c.done(); c.next())
         c->setGCMaxMallocBytes(value);
 }
 
 void
-JSRuntime::updateMallocCounter(JSContext *cx, size_t nbytes)
+JSRuntime::updateMallocCounter(JSCompartment *comp, size_t nbytes)
 {
     /* We tolerate any thread races when updating gcMallocBytes. */
     ptrdiff_t oldCount = gcMallocBytes;
     ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes);
     gcMallocBytes = newCount;
     if (JS_UNLIKELY(newCount <= 0 && oldCount > 0))
         onTooMuchMalloc();
-    else if (cx && cx->compartment)
-        cx->compartment->updateMallocCounter(nbytes);
+    else if (comp)
+        comp->updateMallocCounter(nbytes);
 }
 
 JS_FRIEND_API(void)
 JSRuntime::onTooMuchMalloc()
 {
     TriggerGC(this, gcreason::TOO_MUCH_MALLOC);
 }
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -462,16 +462,25 @@ class PerThreadData : public js::PerThre
         SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {}
     };
     js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
 
     bool                gcRelaxRootChecks;
     int                 gcAssertNoGCDepth;
 #endif
 
+    // If Ion code is on the stack, and has called into C++, this will be
+    // aligned to an Ion exit frame.
+    uint8_t             *ionTop;
+    JSContext           *ionJSContext;
+    uintptr_t            ionStackLimit;
+
+    // This points to the most recent Ion activation running on the thread.
+    js::ion::IonActivation  *ionActivation;
+
     /*
      * When this flag is non-zero, any attempt to GC will be skipped. It is used
      * to suppress GC when reporting an OOM (see js_ReportOutOfMemory) and in
      * debugging facilities that cannot tolerate a GC and would rather OOM
      * immediately, such as utilities exposed to GDB. Setting this flag is
      * extremely dangerous and should only be used when in an OOM situation or
      * in non-exposed debugging facilities.
      */
@@ -1056,29 +1065,20 @@ struct JSRuntime : js::RuntimeFriendFiel
     js::ScriptFilenameTable scriptFilenameTable;
 
 #ifdef DEBUG
     size_t              noGCOrAllocationCheck;
 #endif
 
     bool                jitHardening;
 
-    // If Ion code is on the stack, and has called into C++, this will be
-    // aligned to an Ion exit frame.
-    uint8_t             *ionTop;
-    JSContext           *ionJSContext;
-    uintptr_t            ionStackLimit;
-
     void resetIonStackLimit() {
-        ionStackLimit = nativeStackLimit;
+        mainThread.ionStackLimit = mainThread.nativeStackLimit;
     }
 
-    // This points to the most recent Ion activation running on the thread.
-    js::ion::IonActivation  *ionActivation;
-
     // Cache for ion::GetPcScript().
     js::ion::PcScriptCache *ionPcScriptCache;
 
     js::ThreadPool threadPool;
 
     js::CTypesActivityCallback  ctypesActivityCallback;
 
   private:
@@ -1116,93 +1116,71 @@ struct JSRuntime : js::RuntimeFriendFiel
     bool init(uint32_t maxbytes);
 
     JSRuntime *thisFromCtor() { return this; }
 
     /*
      * Call the system malloc while checking for GC memory pressure and
      * reporting OOM error when cx is not null. We will not GC from here.
      */
-    void* malloc_(size_t bytes, JSContext *cx = NULL) {
-        updateMallocCounter(cx, bytes);
-        void *p = js_malloc(bytes);
-        return JS_LIKELY(!!p) ? p : onOutOfMemory(NULL, bytes, cx);
-    }
+    inline void* malloc_(size_t bytes, JSCompartment *comp = NULL, JSContext *cx = NULL);
 
     /*
      * Call the system calloc while checking for GC memory pressure and
      * reporting OOM error when cx is not null. We will not GC from here.
      */
-    void* calloc_(size_t bytes, JSContext *cx = NULL) {
-        updateMallocCounter(cx, bytes);
-        void *p = js_calloc(bytes);
-        return JS_LIKELY(!!p) ? p : onOutOfMemory(reinterpret_cast<void *>(1), bytes, cx);
-    }
+    inline void* calloc_(size_t bytes, JSCompartment *comp = NULL, JSContext *cx = NULL);
+
+    inline void* realloc_(void* p, size_t oldBytes, size_t newBytes, JSCompartment *comp = NULL, JSContext *cx = NULL);
 
-    void* realloc_(void* p, size_t oldBytes, size_t newBytes, JSContext *cx = NULL) {
-        JS_ASSERT(oldBytes < newBytes);
-        updateMallocCounter(cx, newBytes - oldBytes);
-        void *p2 = js_realloc(p, newBytes);
-        return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, newBytes, cx);
-    }
+    inline void* realloc_(void* p, size_t bytes, JSCompartment *comp = NULL, JSContext *cx = NULL);
 
-    void* realloc_(void* p, size_t bytes, JSContext *cx = NULL) {
-        /*
-         * For compatibility we do not account for realloc that increases
-         * previously allocated memory.
-         */
-        if (!p)
-            updateMallocCounter(cx, bytes);
-        void *p2 = js_realloc(p, bytes);
-        return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, bytes, cx);
+    template <class T>
+    T *pod_malloc(JSCompartment *comp = NULL, JSContext *cx = NULL) {
+        return (T *)malloc_(sizeof(T), comp, cx);
     }
 
     template <class T>
-    T *pod_malloc(JSContext *cx = NULL) {
-        return (T *)malloc_(sizeof(T), cx);
+    T *pod_calloc(JSCompartment *comp = NULL, JSContext *cx = NULL) {
+        return (T *)calloc_(sizeof(T), comp, cx);
     }
 
     template <class T>
-    T *pod_calloc(JSContext *cx = NULL) {
-        return (T *)calloc_(sizeof(T), cx);
-    }
-
-    template <class T>
-    T *pod_malloc(size_t numElems, JSContext *cx = NULL) {
+    T *pod_malloc(size_t numElems, JSCompartment *comp = NULL, JSContext *cx = NULL) {
         if (numElems & js::tl::MulOverflowMask<sizeof(T)>::result) {
             js_ReportAllocationOverflow(cx);
             return NULL;
         }
-        return (T *)malloc_(numElems * sizeof(T), cx);
+        return (T *)malloc_(numElems * sizeof(T), comp, cx);
     }
 
     template <class T>
-    T *pod_calloc(size_t numElems, JSContext *cx = NULL) {
+    T *pod_calloc(size_t numElems, JSCompartment *comp = NULL, JSContext *cx = NULL) {
         if (numElems & js::tl::MulOverflowMask<sizeof(T)>::result) {
             js_ReportAllocationOverflow(cx);
             return NULL;
         }
-        return (T *)calloc_(numElems * sizeof(T), cx);
+        return (T *)calloc_(numElems * sizeof(T), comp, cx);
     }
 
     JS_DECLARE_NEW_METHODS(new_, malloc_, JS_ALWAYS_INLINE)
 
     void setGCMaxMallocBytes(size_t value);
 
     void resetGCMallocBytes() { gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); }
 
     /*
      * Call this after allocating memory held by GC things, to update memory
      * pressure counters or report the OOM error if necessary. If oomError and
      * cx is not null the function also reports OOM error.
      *
      * The function must be called outside the GC lock and in case of OOM error
      * the caller must ensure that no deadlock possible during OOM reporting.
      */
-    void updateMallocCounter(JSContext *cx, size_t nbytes);
+    void updateMallocCounter(JSCompartment *comp, size_t nbytes);
 
     bool isTooMuchMalloc() const {
         return gcMallocBytes <= 0;
     }
 
     /*
      * The function must be called outside the GC lock.
      */
@@ -1394,16 +1372,18 @@ FreeOp::free_(void* p) {
 
 struct JSContext : js::ContextFriendFields,
                    public mozilla::LinkedListElement<JSContext>
 {
     explicit JSContext(JSRuntime *rt);
     JSContext *thisDuringConstruction() { return this; }
     ~JSContext();
 
+    js::PerThreadData& mainThread() { return runtime->mainThread; }
+
   private:
     /* See JSContext::findVersion. */
     JSVersion           defaultVersion;      /* script compilation version */
     JSVersion           versionOverride;     /* supercedes defaultVersion when valid */
     bool                hasVersionOverride;
 
     /* Exception state -- the exception member is a GC root by definition. */
     bool                throwing;            /* is there a pending exception? */
@@ -1665,46 +1645,46 @@ struct JSContext : js::ContextFriendFiel
     /* Innermost-executing generator or null if no generator are executing. */
     JSGenerator *innermostGenerator_;
   public:
     JSGenerator *innermostGenerator() const { return innermostGenerator_; }
     void enterGenerator(JSGenerator *gen);
     void leaveGenerator(JSGenerator *gen);
 
     inline void* malloc_(size_t bytes) {
-        return runtime->malloc_(bytes, this);
+        return runtime->malloc_(bytes, compartment, this);
     }
 
     inline void* calloc_(size_t bytes) {
-        return runtime->calloc_(bytes, this);
+        return runtime->calloc_(bytes, compartment, this);
     }
 
     inline void* realloc_(void* p, size_t bytes) {
-        return runtime->realloc_(p, bytes, this);
+        return runtime->realloc_(p, bytes, compartment, this);
     }
 
     inline void* realloc_(void* p, size_t oldBytes, size_t newBytes) {
-        return runtime->realloc_(p, oldBytes, newBytes, this);
+        return runtime->realloc_(p, oldBytes, newBytes, compartment, this);
     }
 
     template <class T> T *pod_malloc() {
-        return runtime->pod_malloc<T>(this);
+        return runtime->pod_malloc<T>(compartment, this);
     }
 
     template <class T> T *pod_calloc() {
-        return runtime->pod_calloc<T>(this);
+        return runtime->pod_calloc<T>(compartment, this);
     }
 
     template <class T> T *pod_malloc(size_t numElems) {
-        return runtime->pod_malloc<T>(numElems, this);
+        return runtime->pod_malloc<T>(numElems, compartment, this);
     }
 
     template <class T>
     T *pod_calloc(size_t numElems) {
-        return runtime->pod_calloc<T>(numElems, this);
+        return runtime->pod_calloc<T>(numElems, compartment, this);
     }
 
     JS_DECLARE_NEW_METHODS(new_, malloc_, JS_ALWAYS_INLINE)
 
     void purge();
 
     bool isExceptionPending() {
         return throwing;
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -17,16 +17,54 @@
 #include "jsgc.h"
 
 #include "builtin/Object.h" // For js::obj_construct
 #include "frontend/ParseMaps.h"
 #include "vm/RegExpObject.h"
 
 #include "jsgcinlines.h"
 
+void*
+JSRuntime::malloc_(size_t bytes, JSCompartment *comp, JSContext *cx) {
+    JS_ASSERT_IF(cx != NULL, cx->compartment == comp);
+    updateMallocCounter(comp, bytes);
+    void *p = js_malloc(bytes);
+    return JS_LIKELY(!!p) ? p : onOutOfMemory(NULL, bytes, cx);
+}
+
+void*
+JSRuntime::calloc_(size_t bytes, JSCompartment *comp, JSContext *cx) {
+    JS_ASSERT_IF(cx != NULL, cx->compartment == comp);
+    updateMallocCounter(comp, bytes);
+    void *p = js_calloc(bytes);
+    return JS_LIKELY(!!p) ? p : onOutOfMemory(reinterpret_cast<void *>(1), bytes, cx);
+}
+
+void*
+JSRuntime::realloc_(void* p, size_t oldBytes, size_t newBytes, JSCompartment *comp, JSContext *cx) {
+    JS_ASSERT_IF(cx != NULL, cx->compartment == comp);
+    JS_ASSERT(oldBytes < newBytes);
+    updateMallocCounter(comp, newBytes - oldBytes);
+    void *p2 = js_realloc(p, newBytes);
+    return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, newBytes, cx);
+}
+
+void*
+JSRuntime::realloc_(void* p, size_t bytes, JSCompartment *comp, JSContext *cx) {
+    JS_ASSERT_IF(cx != NULL, cx->compartment == comp);
+    /*
+     * For compatibility we do not account for realloc that increases
+     * previously allocated memory.
+     */
+    if (!p)
+        updateMallocCounter(comp, bytes);
+    void *p2 = js_realloc(p, bytes);
+    return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, bytes, cx);
+}
+
 namespace js {
 
 inline void
 NewObjectCache::staticAsserts()
 {
     JS_STATIC_ASSERT(NewObjectCache::MAX_OBJ_SIZE == sizeof(JSObject_Slots16));
     JS_STATIC_ASSERT(gc::FINALIZE_OBJECT_LAST == gc::FINALIZE_OBJECT16_BACKGROUND);
 }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -22,16 +22,17 @@
 #include "gc/Marking.h"
 #include "gc/Root.h"
 #include "js/MemoryMetrics.h"
 #include "methodjit/MethodJIT.h"
 #include "methodjit/PolyIC.h"
 #include "methodjit/MonoIC.h"
 #include "methodjit/Retcon.h"
 #include "vm/Debugger.h"
+#include "vm/ForkJoin.h"
 #include "yarr/BumpPointerAllocator.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #ifdef JS_ION
 #include "ion/IonCompartment.h"
 #include "ion/Ion.h"
@@ -46,16 +47,17 @@ using namespace js::gc;
 
 using mozilla::DebugOnly;
 
 JSCompartment::JSCompartment(JSRuntime *rt)
   : rt(rt),
     principals(NULL),
     global_(NULL),
     enterCompartmentDepth(0),
+    allocator(this),
 #ifdef JSGC_GENERATIONAL
     gcNursery(),
     gcStoreBuffer(&gcNursery),
 #endif
     ionUsingBarriers_(false),
     gcScheduled(false),
     gcState(NoGC),
     gcPreserveCode(false),
@@ -69,18 +71,16 @@ JSCompartment::JSCompartment(JSRuntime *
     typeLifoAlloc(LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     data(NULL),
     active(false),
     scheduledForDestruction(false),
     maybeAlive(true),
     lastAnimationTime(0),
     regExps(rt),
     propertyTree(thisForCtor()),
-    gcMallocAndFreeBytes(0),
-    gcTriggerMallocAndFreeBytes(0),
     gcIncomingGrayPointers(NULL),
     gcLiveArrayBuffers(NULL),
     gcWeakMapList(NULL),
     gcGrayRoots(),
     gcMallocBytes(0),
     debugModeBits(rt->debugMode ? DebugFromC : 0),
     rngState(0),
     watchpointMap(NULL),
@@ -576,17 +576,17 @@ JSCompartment::markTypes(JSTracer *trc)
 
     for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         MarkScriptRoot(trc, &script, "mark_types_script");
         JS_ASSERT(script == i.get<JSScript>());
     }
 
     for (size_t thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) {
-        ArenaHeader *aheader = arenas.getFirstArena(static_cast<AllocKind>(thingKind));
+        ArenaHeader *aheader = allocator.arenas.getFirstArena(static_cast<AllocKind>(thingKind));
         if (aheader)
             rt->gcMarker.pushArenaList(aheader);
     }
 
     for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
         types::TypeObject *type = i.get<types::TypeObject>();
         MarkTypeObjectRoot(trc, &type, "mark_types_scan");
         JS_ASSERT(type == i.get<types::TypeObject>());
@@ -1029,8 +1029,14 @@ JSCompartment::sizeOfIncludingThis(JSMal
     *shapesCompartmentTables = baseShapes.sizeOfExcludingThis(mallocSizeOf)
                              + initialShapes.sizeOfExcludingThis(mallocSizeOf)
                              + newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
                              + lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
     *crossCompartmentWrappersArg = crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
     *regexpCompartment = regExps.sizeOfExcludingThis(mallocSizeOf);
     *debuggeesSet = debuggees.sizeOfExcludingThis(mallocSizeOf);
 }
+
+void
+JSCompartment::adoptWorkerAllocator(Allocator *workerAllocator)
+{
+    allocator.arenas.adoptArenas(rt, &workerAllocator->arenas);
+}
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -116,16 +116,51 @@ namespace JS {
 struct TypeInferenceSizes;
 }
 
 namespace js {
 class AutoDebugModeGC;
 class DebugScopes;
 }
 
+namespace js {
+
+/*
+ * Encapsulates the data needed to perform allocation.  Typically
+ * there is precisely one of these per compartment
+ * (|compartment.allocator|).  However, in parallel execution mode,
+ * there will be one per worker thread.  In general, if a piece of
+ * code must perform execution and should work safely either in
+ * parallel or sequential mode, you should make it take an
+ * |Allocator*| rather than a |JSContext*|.
+ */
+class Allocator
+{
+    JSCompartment*const compartment;
+
+  public:
+    explicit Allocator(JSCompartment *compartment);
+
+    js::gc::ArenaLists arenas;
+
+    inline void *parallelNewGCThing(gc::AllocKind thingKind, size_t thingSize);
+
+    inline void* malloc_(size_t bytes);
+    inline void* calloc_(size_t bytes);
+    inline void* realloc_(void* p, size_t bytes);
+    inline void* realloc_(void* p, size_t oldBytes, size_t newBytes);
+    template <class T> inline T *pod_malloc();
+    template <class T> inline T *pod_calloc();
+    template <class T> inline T *pod_malloc(size_t numElems);
+    template <class T> inline T *pod_calloc(size_t numElems);
+    JS_DECLARE_NEW_METHODS(new_, malloc_, JS_ALWAYS_INLINE)
+};
+
+}
+
 struct JSCompartment : private JS::shadow::Compartment, public js::gc::GraphNodeBase<JSCompartment>
 {
     JSRuntime                    *rt;
     JSPrincipals                 *principals;
 
   private:
     friend struct JSRuntime;
     friend struct JSContext;
@@ -152,17 +187,24 @@ struct JSCompartment : private JS::shado
 
     void initGlobal(js::GlobalObject &global) {
         JS_ASSERT(global.compartment() == this);
         JS_ASSERT(!global_);
         global_ = &global;
     }
 
   public:
-    js::gc::ArenaLists           arenas;
+    js::Allocator                    allocator;
+
+    /*
+     * Moves all data from the allocator |workerAllocator|, which was
+     * in use by a parallel worker, into the compartment's main
+     * allocator.  This is used at the end of a parallel section.
+     */
+    void adoptWorkerAllocator(js::Allocator *workerAllocator);
 
 #ifdef JSGC_GENERATIONAL
     js::gc::Nursery              gcNursery;
     js::gc::StoreBuffer          gcStoreBuffer;
 #endif
 
   private:
     bool                         ionUsingBarriers_;
@@ -354,25 +396,16 @@ struct JSCompartment : private JS::shado
     /*
      * Hash table of all manually call site-cloned functions from within
      * self-hosted code. Cloning according to call site provides extra
      * sensitivity for type specialization and inlining.
      */
     js::CallsiteCloneTable callsiteClones;
     void sweepCallsiteClones();
 
-    /*
-     * Keeps track of the total number of malloc bytes connected to a
-     * compartment's GC things. This counter should be used in preference to
-     * gcMallocBytes. These counters affect collection in the same way as
-     * gcBytes and gcTriggerBytes.
-     */
-    size_t                       gcMallocAndFreeBytes;
-    size_t                       gcTriggerMallocAndFreeBytes;
-
     /* During GC, stores the index of this compartment in rt->compartments. */
     unsigned                     gcIndex;
 
     /*
      * During GC, stores the head of a list of incoming pointers from gray cells.
      *
      * The objects in the list are either cross-compartment wrappers, or
      * debugger wrapper objects.  The list link is either in the second extra
@@ -439,44 +472,39 @@ struct JSCompartment : private JS::shado
     void discardJitCode(js::FreeOp *fop, bool discardConstraints);
     bool isDiscardingJitCode(JSTracer *trc);
     void sweep(js::FreeOp *fop, bool releaseTypes);
     void sweepCrossCompartmentWrappers();
     void purge();
 
     void findOutgoingEdges(js::gc::ComponentFinder<JSCompartment> &finder);
 
-    void setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, js::JSGCInvocationKind gckind);
+    void setGCLastBytes(size_t lastBytes, js::JSGCInvocationKind gckind);
     void reduceGCTriggerBytes(size_t amount);
 
     void resetGCMallocBytes();
     void setGCMaxMallocBytes(size_t value);
     void updateMallocCounter(size_t nbytes) {
+        /*
+         * Note: this code may be run from worker threads.  We
+         * tolerate any thread races when updating gcMallocBytes.
+         */
         ptrdiff_t oldCount = gcMallocBytes;
         ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes);
         gcMallocBytes = newCount;
         if (JS_UNLIKELY(newCount <= 0 && oldCount > 0))
             onTooMuchMalloc();
     }
 
     bool isTooMuchMalloc() const {
         return gcMallocBytes <= 0;
      }
 
     void onTooMuchMalloc();
 
-    void mallocInCompartment(size_t nbytes) {
-        gcMallocAndFreeBytes += nbytes;
-    }
-
-    void freeInCompartment(size_t nbytes) {
-        JS_ASSERT(gcMallocAndFreeBytes >= nbytes);
-        gcMallocAndFreeBytes -= nbytes;
-    }
-
     js::DtoaCache dtoaCache;
 
     /* Random number generator state, used by jsmath.cpp. */
     uint64_t rngState;
 
   private:
     /*
      * Weak reference to each global in this compartment that is a debuggee.
--- a/js/src/jscompartmentinlines.h
+++ b/js/src/jscompartmentinlines.h
@@ -22,9 +22,67 @@ js::AutoCompartment::AutoCompartment(JSC
     cx_->enterCompartment(target->compartment());
 }
 
 js::AutoCompartment::~AutoCompartment()
 {
     cx_->leaveCompartment(origin_);
 }
 
+inline void *
+js::Allocator::malloc_(size_t bytes)
+{
+    return compartment->rt->malloc_(bytes, compartment);
+}
+
+inline void *
+js::Allocator::calloc_(size_t bytes)
+{
+    return compartment->rt->calloc_(bytes, compartment);
+}
+
+inline void *
+js::Allocator::realloc_(void *p, size_t bytes)
+{
+    return compartment->rt->realloc_(p, bytes, compartment);
+}
+
+inline void *
+js::Allocator::realloc_(void* p, size_t oldBytes, size_t newBytes)
+{
+    return compartment->rt->realloc_(p, oldBytes, newBytes, compartment);
+}
+
+template <class T>
+inline T *
+js::Allocator::pod_malloc()
+{
+    return compartment->rt->pod_malloc<T>(compartment);
+}
+
+template <class T>
+inline T *
+js::Allocator::pod_calloc()
+{
+    return compartment->rt->pod_calloc<T>(compartment);
+}
+
+template <class T>
+inline T *
+js::Allocator::pod_malloc(size_t numElems)
+{
+    return compartment->rt->pod_malloc<T>(numElems, compartment);
+}
+
+template <class T>
+inline T *
+js::Allocator::pod_calloc(size_t numElems)
+{
+    return compartment->rt->pod_calloc<T>(numElems, compartment);
+}
+
+inline void *
+js::Allocator::parallelNewGCThing(gc::AllocKind thingKind, size_t thingSize)
+{
+    return arenas.parallelAllocate(compartment, thingKind, thingSize);
+}
+
 #endif /* jscompartment_inlines_h___ */
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -18,19 +18,21 @@
 #include "builtin/TestingFunctions.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace JS;
 
 // Required by PerThreadDataFriendFields::getMainThread()
-JS_STATIC_ASSERT(offsetof(JSRuntime, mainThread) == sizeof(RuntimeFriendFields));
+JS_STATIC_ASSERT(offsetof(JSRuntime, mainThread) ==
+                 PerThreadDataFriendFields::RuntimeMainThreadOffset);
 
 PerThreadDataFriendFields::PerThreadDataFriendFields()
+  : nativeStackLimit(0)
 {
 #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
     skipGCRooters = NULL;
 #endif
 }
 
 JS_FRIEND_API(void)
 JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -564,17 +564,17 @@ IsObjectInContextCompartment(RawObject o
 #define JSITER_KEYVALUE   0x4   /* destructuring for-in wants [key, value] */
 #define JSITER_OWNONLY    0x8   /* iterate over obj's own properties only */
 #define JSITER_HIDDEN     0x10  /* also enumerate non-enumerable properties */
 #define JSITER_FOR_OF     0x20  /* harmony for-of loop */
 
 inline uintptr_t
 GetNativeStackLimit(const JSRuntime *rt)
 {
-    return RuntimeFriendFields::get(rt)->nativeStackLimit;
+    return PerThreadDataFriendFields::getMainThread(rt)->nativeStackLimit;
 }
 
 /*
  * These macros report a stack overflow and run |onerror| if we are close to
  * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a little
  * extra space so that we can ensure that crucial code is able to run.
  */
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -77,16 +77,17 @@
 #include "frontend/Parser.h"
 #include "gc/FindSCCs.h"
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "gc/Memory.h"
 #include "methodjit/MethodJIT.h"
 #include "vm/Debugger.h"
 #include "vm/String.h"
+#include "vm/ForkJoin.h"
 #include "ion/IonCode.h"
 #ifdef JS_ION
 # include "ion/IonMacroAssembler.h"
 #include "ion/IonFrameIterator.h"
 #endif
 
 #include "jsgcinlines.h"
 #include "jsinterpinlines.h"
@@ -268,17 +269,17 @@ ArenaHeader::checkSynchronizedWithFreeLi
      * checks in this case.
      */
     if (IsBackgroundFinalized(getAllocKind()) && !compartment->rt->isHeapBusy())
         return;
 
     FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
     if (firstSpan.isEmpty())
         return;
-    const FreeSpan *list = compartment->arenas.getFreeList(getAllocKind());
+    const FreeSpan *list = compartment->allocator.arenas.getFreeList(getAllocKind());
     if (list->isEmpty() || firstSpan.arenaAddress() != list->arenaAddress())
         return;
 
     /*
      * Here this arena has free things, FreeList::lists[thingKind] is not
      * empty and also points to this arena. Thus they must the same.
      */
     JS_ASSERT(firstSpan.isSameNonEmptySpan(list));
@@ -1094,17 +1095,17 @@ static size_t
 ComputeTriggerBytes(JSCompartment *comp, size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind)
 {
     size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, comp->rt->gcAllocationThreshold);
     float trigger = float(base) * comp->gcHeapGrowthFactor;
     return size_t(Min(float(maxBytes), trigger));
 }
 
 void
-JSCompartment::setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, JSGCInvocationKind gckind)
+JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
 {
     /*
      * The heap growth factor depends on the heap size after a GC and the GC frequency.
      * For low frequency GCs (more than 1sec between GCs) we let the heap grow to 150%.
      * For high frequency GCs we let the heap grow depending on the heap size:
      *   lastBytes < highFrequencyLowLimit: 300%
      *   lastBytes > highFrequencyHighLimit: 150%
      *   otherwise: linear interpolation between 150% and 300% based on lastBytes
@@ -1132,29 +1133,32 @@ JSCompartment::setGCLastBytes(size_t las
             }
             rt->gcHighFrequencyGC = true;
         } else {
             gcHeapGrowthFactor = rt->gcLowFrequencyHeapGrowth;
             rt->gcHighFrequencyGC = false;
         }
     }
     gcTriggerBytes = ComputeTriggerBytes(this, lastBytes, rt->gcMaxBytes, gckind);
-    gcTriggerMallocAndFreeBytes = ComputeTriggerBytes(this, lastMallocBytes, SIZE_MAX, gckind);
 }
 
 void
 JSCompartment::reduceGCTriggerBytes(size_t amount)
 {
     JS_ASSERT(amount > 0);
     JS_ASSERT(gcTriggerBytes >= amount);
     if (gcTriggerBytes - amount < rt->gcAllocationThreshold * gcHeapGrowthFactor)
         return;
     gcTriggerBytes -= amount;
 }
 
+Allocator::Allocator(JSCompartment *compartment)
+  : compartment(compartment)
+{}
+
 inline void
 ArenaLists::prepareForIncrementalGC(JSRuntime *rt)
 {
     for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
         FreeSpan *headSpan = &freeLists[i];
         if (!headSpan->isEmpty()) {
             ArenaHeader *aheader = headSpan->arenaHeader();
             aheader->allocatedDuringIncremental = true;
@@ -1165,19 +1169,46 @@ ArenaLists::prepareForIncrementalGC(JSRu
 
 static inline void
 PushArenaAllocatedDuringSweep(JSRuntime *runtime, ArenaHeader *arena)
 {
     arena->setNextAllocDuringSweep(runtime->gcArenasAllocatedDuringSweep);
     runtime->gcArenasAllocatedDuringSweep = arena;
 }
 
+void *
+ArenaLists::parallelAllocate(JSCompartment *comp, AllocKind thingKind, size_t thingSize)
+{
+    /*
+     * During parallel Rivertrail sections, no GC is permitted. If no
+     * existing arena can satisfy the allocation, then a new one is
+     * allocated. If that fails, then we return NULL which will cause
+     * the parallel section to abort.
+     */
+
+    void *t = allocateFromFreeList(thingKind, thingSize);
+    if (t)
+        return t;
+
+    return allocateFromArena(comp, thingKind);
+}
+
 inline void *
 ArenaLists::allocateFromArena(JSCompartment *comp, AllocKind thingKind)
 {
+    /*
+     * Parallel JS Note:
+     *
+     * This function can be called from parallel threads all of which
+     * are associated with the same compartment. In that case, each
+     * thread will have a distinct ArenaLists.  Therefore, whenever we
+     * fall through to PickChunk() we must be sure that we are holding
+     * a lock.
+     */
+
     Chunk *chunk = NULL;
 
     ArenaList *al = &arenaLists[thingKind];
     AutoLockGC maybeLock;
 
 #ifdef JS_THREADSAFE
     volatile uintptr_t *bfs = &backgroundFinalizeState[thingKind];
     if (*bfs != BFS_DONE) {
@@ -1345,17 +1376,17 @@ ArenaLists::backgroundFinalize(FreeOp *f
     FinalizeArenas(fop, &listHead, finalized, thingKind, budget);
     JS_ASSERT(!listHead);
 
     /*
      * After we finish the finalization al->cursor must point to the end of
      * the head list as we emptied the list before the background finalization
      * and the allocation adds new arenas before the cursor.
      */
-    ArenaLists *lists = &comp->arenas;
+    ArenaLists *lists = &comp->allocator.arenas;
     ArenaList *al = &lists->arenaLists[thingKind];
 
     AutoLockGC lock(fop->runtime());
     JS_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN);
     JS_ASSERT(!*al->cursor);
 
     if (finalized.head) {
         *al->cursor = finalized.head;
@@ -1434,64 +1465,74 @@ ArenaLists::queueShapesForSweep(FreeOp *
 
 void
 ArenaLists::queueIonCodeForSweep(FreeOp *fop)
 {
     gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_IONCODE);
     finalizeNow(fop, FINALIZE_IONCODE);
 }
 
-static void
-RunLastDitchGC(JSContext *cx, gcreason::Reason reason)
+static void*
+RunLastDitchGC(JSContext *cx, JSCompartment *comp, AllocKind thingKind)
 {
+    /*
+     * In parallel sections, we do not attempt to refill the free list
+     * and hence do not encounter last ditch GC.
+     */
+    JS_ASSERT(!ForkJoinSlice::InParallelSection());
+
+    PrepareCompartmentForGC(comp);
+
     JSRuntime *rt = cx->runtime;
 
     /* The last ditch GC preserves all atoms. */
     AutoKeepAtoms keep(rt);
-    GC(rt, GC_NORMAL, reason);
+    GC(rt, GC_NORMAL, gcreason::LAST_DITCH);
+
+    /*
+     * The JSGC_END callback can legitimately allocate new GC
+     * things and populate the free list. If that happens, just
+     * return that list head.
+     */
+    size_t thingSize = Arena::thingSize(thingKind);
+    if (void *thing = comp->allocator.arenas.allocateFromFreeList(thingKind, thingSize))
+        return thing;
+
+    return NULL;
 }
 
 template <AllowGC allowGC>
 /* static */ void *
 ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind)
 {
-    JS_ASSERT(cx->compartment->arenas.freeLists[thingKind].isEmpty());
+    JS_ASSERT(cx->compartment->allocator.arenas.freeLists[thingKind].isEmpty());
 
     JSCompartment *comp = cx->compartment;
     JSRuntime *rt = comp->rt;
     JS_ASSERT(!rt->isHeapBusy());
 
     bool runGC = rt->gcIncrementalState != NO_INCREMENTAL &&
                  comp->gcBytes > comp->gcTriggerBytes &&
                  allowGC;
     for (;;) {
         if (JS_UNLIKELY(runGC)) {
-            PrepareCompartmentForGC(comp);
-            RunLastDitchGC(cx, gcreason::LAST_DITCH);
-
-            /*
-             * The JSGC_END callback can legitimately allocate new GC
-             * things and populate the free list. If that happens, just
-             * return that list head.
-             */
-            size_t thingSize = Arena::thingSize(thingKind);
-            if (void *thing = comp->arenas.allocateFromFreeList(thingKind, thingSize))
+            if (void *thing = RunLastDitchGC(cx, comp, thingKind))
                 return thing;
         }
 
         /*
          * allocateFromArena may fail while the background finalization still
          * run. In that case we want to wait for it to finish and restart.
          * However, checking for that is racy as the background finalization
          * could free some things after allocateFromArena decided to fail but
          * at this point it may have already stopped. To avoid this race we
          * always try to allocate twice.
          */
         for (bool secondAttempt = false; ; secondAttempt = true) {
-            void *thing = comp->arenas.allocateFromArena(comp, thingKind);
+            void *thing = comp->allocator.arenas.allocateFromArena(comp, thingKind);
             if (JS_LIKELY(!!thing))
                 return thing;
             if (secondAttempt)
                 break;
 
             rt->gcHelperThread.waitBackgroundSweepEnd();
         }
 
@@ -1906,28 +1947,40 @@ TriggerOperationCallback(JSRuntime *rt, 
     rt->gcIsNeeded = true;
     rt->gcTriggerReason = reason;
     rt->triggerOperationCallback();
 }
 
 void
 js::TriggerGC(JSRuntime *rt, gcreason::Reason reason)
 {
+    /* Wait till end of parallel section to trigger GC. */
+    if (ForkJoinSlice *slice = ForkJoinSlice::Current()) {
+        slice->requestGC(reason);
+        return;
+    }
+
     rt->assertValidThread();
 
     if (rt->isHeapBusy())
         return;
 
     PrepareForFullGC(rt);
     TriggerOperationCallback(rt, reason);
 }
 
 void
 js::TriggerCompartmentGC(JSCompartment *comp, gcreason::Reason reason)
 {
+    /* Wait till end of parallel section to trigger GC. */
+    if (ForkJoinSlice *slice = ForkJoinSlice::Current()) {
+        slice->requestCompartmentGC(comp, reason);
+        return;
+    }
+
     JSRuntime *rt = comp->rt;
     rt->assertValidThread();
 
     if (rt->isHeapBusy())
         return;
 
     if (rt->gcZeal() == ZealAllocValue) {
         TriggerGC(rt, reason);
@@ -1968,22 +2021,16 @@ js::MaybeGC(JSContext *cx)
         rt->gcIncrementalState == NO_INCREMENTAL &&
         !rt->gcHelperThread.sweeping())
     {
         PrepareCompartmentForGC(comp);
         GCSlice(rt, GC_NORMAL, gcreason::MAYBEGC);
         return;
     }
 
-    if (comp->gcMallocAndFreeBytes > comp->gcTriggerMallocAndFreeBytes) {
-        PrepareCompartmentForGC(comp);
-        GCSlice(rt, GC_NORMAL, gcreason::MAYBEGC);
-        return;
-    }
-
 #ifndef JS_MORE_DETERMINISTIC
     /*
      * Access to the counters and, on 32 bit, setting gcNextFullGCTime below
      * is not atomic and a race condition could trigger or suppress the GC. We
      * tolerate this.
      */
     int64_t now = PRMJ_Now();
     if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) {
@@ -2135,35 +2182,35 @@ SweepBackgroundThings(JSRuntime* rt, boo
      * We must finalize in the correct order, see comments in
      * finalizeObjects.
      */
     FreeOp fop(rt, false);
     for (int phase = 0 ; phase < BackgroundPhaseCount ; ++phase) {
         for (JSCompartment *comp = rt->gcSweepingCompartments; comp; comp = comp->gcNextGraphNode) {
             for (int index = 0 ; index < BackgroundPhaseLength[phase] ; ++index) {
                 AllocKind kind = BackgroundPhases[phase][index];
-                ArenaHeader *arenas = comp->arenas.arenaListsToSweep[kind];
+                ArenaHeader *arenas = comp->allocator.arenas.arenaListsToSweep[kind];
                 if (arenas)
                     ArenaLists::backgroundFinalize(&fop, arenas, onBackgroundThread);
             }
         }
     }
 
     rt->gcSweepingCompartments = NULL;
 }
 
 #ifdef JS_THREADSAFE
 static void
 AssertBackgroundSweepingFinished(JSRuntime *rt)
 {
     JS_ASSERT(!rt->gcSweepingCompartments);
     for (CompartmentsIter c(rt); !c.done(); c.next()) {
         for (unsigned i = 0 ; i < FINALIZE_LIMIT ; ++i) {
-            JS_ASSERT(!c->arenas.arenaListsToSweep[i]);
-            JS_ASSERT(c->arenas.doneBackgroundFinalize(AllocKind(i)));
+            JS_ASSERT(!c->allocator.arenas.arenaListsToSweep[i]);
+            JS_ASSERT(c->allocator.arenas.doneBackgroundFinalize(AllocKind(i)));
         }
     }
 }
 
 unsigned
 js::GetCPUCount()
 {
     static unsigned ncpus = 0;
@@ -2487,19 +2534,19 @@ SweepCompartments(FreeOp *fop, bool last
     JSCompartment **write = read;
     JS_ASSERT(rt->compartments.length() >= 1);
     JS_ASSERT(*rt->compartments.begin() == rt->atomsCompartment);
 
     while (read < end) {
         JSCompartment *compartment = *read++;
 
         if (!compartment->hold && compartment->wasGCStarted() &&
-            (compartment->arenas.arenaListsAreEmpty() || lastGC))
+            (compartment->allocator.arenas.arenaListsAreEmpty() || lastGC))
         {
-            compartment->arenas.checkEmptyFreeLists();
+            compartment->allocator.arenas.checkEmptyFreeLists();
             if (callback)
                 callback(fop, compartment);
             if (compartment->principals)
                 JS_DropPrincipals(rt, compartment->principals);
             fop->delete_(compartment);
             continue;
         }
         *write++ = compartment;
@@ -2619,17 +2666,17 @@ BeginMarkPhase(JSRuntime *rt)
 #endif
 
     rt->gcIsFull = true;
     bool any = false;
     for (CompartmentsIter c(rt); !c.done(); c.next()) {
         /* Assert that compartment state is as we expect */
         JS_ASSERT(!c->isCollecting());
         for (unsigned i = 0; i < FINALIZE_LIMIT; ++i)
-            JS_ASSERT(!c->arenas.arenaListsToSweep[i]);
+            JS_ASSERT(!c->allocator.arenas.arenaListsToSweep[i]);
         JS_ASSERT(!c->gcLiveArrayBuffers);
 
         /* Set up which compartments will be collected. */
         if (c->isGCScheduled()) {
             if (c != rt->atomsCompartment) {
                 any = true;
                 c->setGCState(JSCompartment::Mark);
             }
@@ -2663,17 +2710,17 @@ BeginMarkPhase(JSRuntime *rt)
      * At the end of each incremental slice, we call prepareForIncrementalGC,
      * which marks objects in all arenas that we're currently allocating
      * into. This can cause leaks if unreachable objects are in these
      * arenas. This purge call ensures that we only mark arenas that have had
      * allocations after the incremental GC started.
      */
     if (rt->gcIsIncremental) {
         for (GCCompartmentsIter c(rt); !c.done(); c.next())
-            c->arenas.purge();
+            c->allocator.arenas.purge();
     }
 
     rt->gcMarker.start();
     JS_ASSERT(!rt->gcMarker.callback);
     JS_ASSERT(IS_GC_MARKING_TRACER(&rt->gcMarker));
 
     /* For non-incremental GC the following sweep discards the jit code. */
     if (rt->gcIsIncremental) {
@@ -2704,17 +2751,17 @@ BeginMarkPhase(JSRuntime *rt)
     /*
      * Mark phase.
      */
     gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_MARK);
     gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_MARK_ROOTS);
 
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         /* Unmark everything in the compartments being collected. */
-        c->arenas.unmarkAll();
+        c->allocator.arenas.unmarkAll();
 
         /* Reset weak map list for the compartments being collected. */
         WeakMapBase::resetCompartmentWeakMapList(c);
     }
 
     MarkRuntime(gcmarker);
     BufferGrayRoots(gcmarker);
 
@@ -3164,17 +3211,17 @@ JSCompartment::findOutgoingEdges(Compone
     }
 
     Debugger::findCompartmentEdges(this, finder);
 }
 
 static void
 FindCompartmentGroups(JSRuntime *rt)
 {
-    ComponentFinder<JSCompartment> finder(rt->nativeStackLimit);
+    ComponentFinder<JSCompartment> finder(rt->mainThread.nativeStackLimit);
     if (!rt->gcIsIncremental)
         finder.useOneComponent();
 
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         JS_ASSERT(c->isGCMarking());
         finder.addNode(c);
     }
     rt->gcCompartmentGroups = finder.getResultsList();
@@ -3497,17 +3544,17 @@ BeginSweepingCompartmentGroup(JSRuntime 
 
     bool sweepingAtoms = false;
     for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
         /* Set the GC state to sweeping. */
         JS_ASSERT(c->isGCMarking());
         c->setGCState(JSCompartment::Sweep);
 
         /* Purge the ArenaLists before sweeping. */
-        c->arenas.purge();
+        c->allocator.arenas.purge();
 
         if (c == rt->atomsCompartment)
             sweepingAtoms = true;
     }
 
     ValidateIncrementalMarking(rt);
 
     FreeOp fop(rt, rt->gcSweepOnBackgroundThread);
@@ -3548,34 +3595,34 @@ BeginSweepingCompartmentGroup(JSRuntime 
      * foreground or on the background thread.
      *
      * Note that order is important here for the background case.
      *
      * Objects are finalized immediately but this may change in the future.
      */
     for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
         gcstats::AutoSCC scc(rt->gcStats, rt->gcCompartmentGroupIndex);
-        c->arenas.queueObjectsForSweep(&fop);
+        c->allocator.arenas.queueObjectsForSweep(&fop);
     }
     for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
         gcstats::AutoSCC scc(rt->gcStats, rt->gcCompartmentGroupIndex);
-        c->arenas.queueStringsForSweep(&fop);
+        c->allocator.arenas.queueStringsForSweep(&fop);
     }
     for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
     	gcstats::AutoSCC scc(rt->gcStats, rt->gcCompartmentGroupIndex);
-        c->arenas.queueScriptsForSweep(&fop);
+        c->allocator.arenas.queueScriptsForSweep(&fop);
     }
     for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
         gcstats::AutoSCC scc(rt->gcStats, rt->gcCompartmentGroupIndex);
-        c->arenas.queueShapesForSweep(&fop);
+        c->allocator.arenas.queueShapesForSweep(&fop);
     }
 #ifdef JS_ION
     for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
         gcstats::AutoSCC scc(rt->gcStats, rt->gcCompartmentGroupIndex);
-        c->arenas.queueIonCodeForSweep(&fop);
+        c->allocator.arenas.queueIonCodeForSweep(&fop);
     }
 #endif
 
     rt->gcSweepPhase = 0;
     rt->gcSweepCompartment = rt->gcCurrentCompartmentGroup;
     rt->gcSweepKindIndex = 0;
 
     {
@@ -3673,17 +3720,17 @@ SweepPhase(JSRuntime *rt, SliceBudget &s
             for (; rt->gcSweepCompartment;
                  rt->gcSweepCompartment = rt->gcSweepCompartment->nextNodeInGroup())
             {
                 JSCompartment *c = rt->gcSweepCompartment;
 
                 while (rt->gcSweepKindIndex < FinalizePhaseLength[rt->gcSweepPhase]) {
                     AllocKind kind = FinalizePhases[rt->gcSweepPhase][rt->gcSweepKindIndex];
 
-                    if (!c->arenas.foregroundFinalize(&fop, kind, sliceBudget))
+                    if (!c->allocator.arenas.foregroundFinalize(&fop, kind, sliceBudget))
                         return false;  /* Yield to the mutator. */
 
                     ++rt->gcSweepKindIndex;
                 }
                 rt->gcSweepKindIndex = 0;
             }
             rt->gcSweepCompartment = rt->gcCurrentCompartmentGroup;
         }
@@ -3725,17 +3772,17 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocat
      * If we found any black->gray edges during marking, we completely clear the
      * mark bits of all uncollected compartments, or if a reset has occured, compartments that
      * will no longer be collected. This is safe, although it may
      * prevent the cycle collector from collecting some dead objects.
      */
     if (rt->gcFoundBlackGrayEdges) {
         for (CompartmentsIter c(rt); !c.done(); c.next()) {
             if (!c->isCollecting())
-                c->arenas.unmarkAll();
+                c->allocator.arenas.unmarkAll();
         }
     }
 
 #ifdef DEBUG
     PropertyTree::dumpShapes(rt);
 #endif
 
     {
@@ -3800,17 +3847,17 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocat
         rt->freeLifoAlloc.freeAll();
 
         /* Ensure the compartments get swept if it's the last GC. */
         if (lastGC)
             SweepCompartments(&fop, lastGC);
     }
 
     for (CompartmentsIter c(rt); !c.done(); c.next()) {
-        c->setGCLastBytes(c->gcBytes, c->gcMallocAndFreeBytes, gckind);
+        c->setGCLastBytes(c->gcBytes, gckind);
         if (c->isCollecting()) {
             JS_ASSERT(c->isGCFinished());
             c->setGCState(JSCompartment::NoGC);
         }
 
 #ifdef DEBUG
         JS_ASSERT(!c->isCollecting());
         JS_ASSERT(!c->wasGCStarted());
@@ -3821,17 +3868,17 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocat
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
             if (e.front().key.kind != CrossCompartmentKey::StringWrapper)
                 AssertNotOnGrayList(&e.front().value.get().toObject());
         }
 
         for (unsigned i = 0 ; i < FINALIZE_LIMIT ; ++i) {
             JS_ASSERT_IF(!IsBackgroundFinalized(AllocKind(i)) ||
                          !rt->gcSweepOnBackgroundThread,
-                         !c->arenas.arenaListsToSweep[i]);
+                         !c->allocator.arenas.arenaListsToSweep[i]);
         }
 #endif
     }
 
     FinishMarkingValidation(rt);
 
     rt->gcLastGCTime = PRMJ_Now();
 }
@@ -3890,23 +3937,23 @@ AutoGCSession::~AutoGCSession()
 
     runtime->resetGCMallocBytes();
 }
 
 AutoCopyFreeListToArenas::AutoCopyFreeListToArenas(JSRuntime *rt)
   : runtime(rt)
 {
     for (CompartmentsIter c(rt); !c.done(); c.next())
-        c->arenas.copyFreeListsToArenas();
+        c->allocator.arenas.copyFreeListsToArenas();
 }
 
 AutoCopyFreeListToArenas::~AutoCopyFreeListToArenas()
 {
     for (CompartmentsIter c(runtime); !c.done(); c.next())
-        c->arenas.clearFreeListsInArenas();
+        c->allocator.arenas.clearFreeListsInArenas();
 }
 
 static void
 IncrementalCollectSlice(JSRuntime *rt,
                         int64_t budget,
                         gcreason::Reason gcReason,
                         JSGCInvocationKind gcKind);
 
@@ -3962,17 +4009,17 @@ ResetIncrementalGC(JSRuntime *rt, const 
     rt->gcStats.reset(reason);
 
 #ifdef DEBUG
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         JS_ASSERT(c->isCollecting());
         JS_ASSERT(!c->needsBarrier());
         JS_ASSERT(!c->gcLiveArrayBuffers);
         for (unsigned i = 0 ; i < FINALIZE_LIMIT ; ++i)
-            JS_ASSERT(!c->arenas.arenaListsToSweep[i]);
+            JS_ASSERT(!c->allocator.arenas.arenaListsToSweep[i]);
     }
 #endif
 }
 
 class AutoGCSlice {
   public:
     AutoGCSlice(JSRuntime *rt);
     ~AutoGCSlice();
@@ -4009,17 +4056,17 @@ AutoGCSlice::AutoGCSlice(JSRuntime *rt)
 }
 
 AutoGCSlice::~AutoGCSlice()
 {
     /* We can't use GCCompartmentsIter if this is the end of the last slice. */
     for (CompartmentsIter c(runtime); !c.done(); c.next()) {
         if (c->isGCMarking()) {
             c->setNeedsBarrier(true, JSCompartment::UpdateIon);
-            c->arenas.prepareForIncrementalGC(runtime);
+            c->allocator.arenas.prepareForIncrementalGC(runtime);
         } else {
             c->setNeedsBarrier(false, JSCompartment::UpdateIon);
         }
     }
 }
 
 static void
 PushZealSelectedObjects(JSRuntime *rt)
@@ -4301,16 +4348,19 @@ ShouldCleanUpEverything(JSRuntime *rt, g
            reason == gcreason::DEBUG_MODE_GC ||
            gckind == GC_SHRINK;
 }
 
 static void
 Collect(JSRuntime *rt, bool incremental, int64_t budget,
         JSGCInvocationKind gckind, gcreason::Reason reason)
 {
+    /* GC shouldn't be running in parallel execution mode */
+    JS_ASSERT(!ForkJoinSlice::InParallelSection());
+
     JS_AbortIfWrongThread(rt);
 
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::GC_START,
                         TraceLogging::GC_STOP);
 #endif
 
@@ -4500,17 +4550,17 @@ gc::NewCompartment(JSContext *cx, JSPrin
     JS_AbortIfWrongThread(rt);
 
     JSCompartment *compartment = cx->new_<JSCompartment>(rt);
     if (compartment && compartment->init(cx)) {
 
         // Set up the principals.
         JS_SetCompartmentPrincipals(compartment, principals);
 
-        compartment->setGCLastBytes(8192, 8192, GC_NORMAL);
+        compartment->setGCLastBytes(8192, GC_NORMAL);
 
         /*
          * Before reporting the OOM condition, |lock| needs to be cleaned up,
          * hence the scoping.
          */
         {
             AutoLockGC lock(rt);
             if (rt->compartments.append(compartment))
@@ -4740,16 +4790,75 @@ js::PurgeJITCaches(JSCompartment *c)
         /* Discard Ion caches. */
         ion::PurgeCaches(script, c);
 
 #endif
     }
 #endif
 }
 
+
+void
+ArenaLists::adoptArenas(JSRuntime *rt, ArenaLists *fromArenaLists)
+{
+    // The other parallel threads have all completed now, and GC
+    // should be inactive, but still take the lock as a kind of read
+    // fence.
+    AutoLockGC lock(rt);
+
+    fromArenaLists->purge();
+
+    for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
+#ifdef JS_THREADSAFE
+        // When we enter a parallel section, we join the background
+        // thread, and we do not run GC while in the parallel section,
+        // so no finalizer should be active!
+        volatile uintptr_t *bfs = &backgroundFinalizeState[thingKind];
+        switch (*bfs) {
+          case BFS_DONE:
+            break;
+          case BFS_JUST_FINISHED:
+            // No allocations between end of last sweep and now.
+            // Transfering over arenas is a kind of allocation.
+            *bfs = BFS_DONE;
+            break;
+          default:
+            JS_ASSERT(!"Background finalization in progress, but it should not be.");
+            break;
+        }
+#endif /* JS_THREADSAFE */
+
+        ArenaList *fromList = &fromArenaLists->arenaLists[thingKind];
+        ArenaList *toList = &arenaLists[thingKind];
+        while (fromList->head != NULL) {
+            ArenaHeader *fromHeader = fromList->head;
+            fromList->head = fromHeader->next;
+            fromHeader->next = NULL;
+
+            toList->insert(fromHeader);
+        }
+    }
+}
+
+bool
+ArenaLists::containsArena(JSRuntime *rt, ArenaHeader *needle)
+{
+    AutoLockGC lock(rt);
+    size_t allocKind = needle->getAllocKind();
+    for (ArenaHeader *aheader = arenaLists[allocKind].head;
+         aheader != NULL;
+         aheader = aheader->next)
+    {
+        if (aheader == needle)
+            return true;
+    }
+    return false;
+}
+
+
 AutoMaybeTouchDeadCompartments::AutoMaybeTouchDeadCompartments(JSContext *cx)
   : runtime(cx->runtime),
     markCount(runtime->gcObjectsMarkedInDeadCompartments),
     inIncremental(IsIncrementalGCInProgress(runtime)),
     manipulatingDeadCompartments(runtime->gcManipulatingDeadCompartments)
 {
     runtime->gcManipulatingDeadCompartments = true;
 }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -409,16 +409,28 @@ struct ArenaLists {
 
     JS_ALWAYS_INLINE void *allocateFromFreeList(AllocKind thingKind, size_t thingSize) {
         return freeLists[thingKind].allocate(thingSize);
     }
 
     template <AllowGC allowGC>
     static void *refillFreeList(JSContext *cx, AllocKind thingKind);
 
+    /*
+     * Moves all arenas from |fromArenaLists| into |this|.  In
+     * parallel blocks, we temporarily create one ArenaLists per
+     * parallel thread.  When the parallel block ends, we move
+     * whatever allocations may have been performed back into the
+     * compartment's main arena list using this function.
+     */
+    void adoptArenas(JSRuntime *runtime, ArenaLists *fromArenaLists);
+
+    /* True if the ArenaHeader in question is found in this ArenaLists */
+    bool containsArena(JSRuntime *runtime, ArenaHeader *arenaHeader);
+
     void checkEmptyFreeLists() {
 #ifdef DEBUG
         for (size_t i = 0; i < mozilla::ArrayLength(freeLists); ++i)
             JS_ASSERT(freeLists[i].isEmpty());
 #endif
     }
 
     void checkEmptyFreeList(AllocKind kind) {
@@ -429,16 +441,24 @@ struct ArenaLists {
     void queueStringsForSweep(FreeOp *fop);
     void queueShapesForSweep(FreeOp *fop);
     void queueScriptsForSweep(FreeOp *fop);
     void queueIonCodeForSweep(FreeOp *fop);
 
     bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget);
     static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgroundThread);
 
+    /*
+     * Invoked from IonMonkey-compiled parallel worker threads to
+     * perform an allocation.  In this case, |this| will be
+     * thread-local, but the compartment |comp| is shared between all
+     * threads.
+     */
+    void *parallelAllocate(JSCompartment *comp, AllocKind thingKind, size_t thingSize);
+
   private:
     inline void finalizeNow(FreeOp *fop, AllocKind thingKind);
     inline void queueForForegroundSweep(FreeOp *fop, AllocKind thingKind);
     inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind);
 
     inline void *allocateFromArena(JSCompartment *comp, AllocKind thingKind);
 };
 
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -228,18 +228,18 @@ class ArenaIter
     }
 
     void init(ArenaHeader *aheaderArg) {
         aheader = aheaderArg;
         remainingHeader = NULL;
     }
 
     void init(JSCompartment *comp, AllocKind kind) {
-        aheader = comp->arenas.getFirstArena(kind);
-        remainingHeader = comp->arenas.getFirstArenaToSweep(kind);
+        aheader = comp->allocator.arenas.getFirstArena(kind);
+        remainingHeader = comp->allocator.arenas.getFirstArenaToSweep(kind);
         if (!aheader) {
             aheader = remainingHeader;
             remainingHeader = NULL;
         }
     }
 
     bool done() {
         return !aheader;
@@ -268,17 +268,17 @@ class CellIterImpl
     uintptr_t thing;
     Cell *cell;
 
   protected:
     CellIterImpl() {
     }
 
     void initSpan(JSCompartment *comp, AllocKind kind) {
-        JS_ASSERT(comp->arenas.isSynchronizedFreeList(kind));
+        JS_ASSERT(comp->allocator.arenas.isSynchronizedFreeList(kind));
         firstThingOffset = Arena::firstThingOffset(kind);
         thingSize = Arena::thingSize(kind);
         firstSpan.initAsEmpty();
         span = &firstSpan;
         thing = span->first;
     }
 
     void init(ArenaHeader *singleAheader) {
@@ -351,17 +351,17 @@ class CellIter : public CellIterImpl
 {
     ArenaLists *lists;
     AllocKind kind;
 #ifdef DEBUG
     size_t *counter;
 #endif
   public:
     CellIter(JSCompartment *comp, AllocKind kind)
-      : lists(&comp->arenas),
+      : lists(&comp->allocator.arenas),
         kind(kind)
     {
         /*
          * We have a single-threaded runtime, so there's no need to protect
          * against other threads iterating or allocating. However, we do have
          * background finalization; make sure people aren't using CellIter to
          * walk such allocation kinds.
          */
@@ -498,17 +498,17 @@ NewGCThing(JSContext *cx, js::gc::AllocK
     if (cx->runtime->needZealousGC() && allowGC)
         js::gc::RunDebugGC(cx);
 #endif
 
     if (allowGC)
         MaybeCheckStackRoots(cx, /* relax = */ false);
 
     JSCompartment *comp = cx->compartment;
-    T *t = static_cast<T *>(comp->arenas.allocateFromFreeList(kind, thingSize));
+    T *t = static_cast<T *>(comp->allocator.arenas.allocateFromFreeList(kind, thingSize));
     if (!t)
         t = static_cast<T *>(js::gc::ArenaLists::refillFreeList<allowGC>(cx, kind));
 
     JS_ASSERT_IF(t && comp->wasGCStarted() && (comp->isGCMarking() || comp->isGCSweeping()),
                  t->arenaHeader()->allocatedDuringIncremental);
 
 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
     if (cx->runtime->gcVerifyPostData && IsNurseryAllocable(kind) && !IsAtomsCompartment(comp))
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -14,16 +14,17 @@
 #include "jsinfer.h"
 #include "jsinterp.h"
 #include "jslibmath.h"
 #include "jsnum.h"
 #include "jsprobes.h"
 #include "jsstr.h"
 
 #include "methodjit/MethodJIT.h"
+#include "vm/ForkJoin.h"
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsinferinlines.h"
 #include "jsopcodeinlines.h"
 #include "jspropertycacheinlines.h"
 #include "jstypedarrayinlines.h"
 
@@ -1130,16 +1131,17 @@ class FastInvokeGuard
     FastInvokeGuard(JSContext *cx, const Value &fval)
       : fun_(cx)
       , script_(cx)
 #ifdef JS_ION
       , ictx_(cx, cx->compartment, NULL)
       , useIon_(ion::IsEnabled(cx))
 #endif
     {
+        JS_ASSERT(!ForkJoinSlice::InParallelSection());
         initFunction(fval);
     }
 
     void initFunction(const Value &fval) {
         if (fval.isObject() && fval.toObject().isFunction()) {
             JSFunction *fun = fval.toObject().toFunction();
             if (fun->hasScript()) {
                 fun_ = fun;
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -40,18 +40,18 @@
 #include "ds/Sort.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "vm/GlobalObject.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
+#include "builtin/Iterator-inl.h"
 #include "builtin/ParallelArray-inl.h"
-#include "builtin/Iterator-inl.h"
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayLength;
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -57,16 +57,17 @@
 
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
 #include "jscntxtinlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
+#include "jscompartmentinlines.h"
 
 #include "vm/BooleanObject-inl.h"
 #include "vm/NumberObject-inl.h"
 #include "vm/StringObject-inl.h"
 
 #include "jsautooplen.h"
 
 using namespace js;
@@ -2517,76 +2518,93 @@ JSObject::willBeSparseElements(unsigned 
             return false;
     }
     return true;
 }
 
 bool
 JSObject::growElements(JSContext *cx, unsigned newcap)
 {
+    size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
+
+    if (!growElements(&cx->compartment->allocator, newcap)) {
+        JS_ReportOutOfMemory(cx);
+        return false;
+    }
+
+    if (Probes::objectResizeActive())
+        Probes::resizeObject(cx, this, oldSize, computedSizeOfThisSlotsElements());
+
+    return true;
+}
+
+bool
+JSObject::growElements(js::Allocator *alloc, unsigned newcap)
+{
+    /*
+     * This version of |growElements()|, which takes a
+     * |js::Allocator*| as opposed to a |JSContext*|, is intended to
+     * run either during sequential or parallel execution.  As per
+     * convention, since it does not take a JSContext*, it does not
+     * report an error on out of memory but simply returns false.
+     */
     JS_ASSERT(isExtensible());
 
     /*
      * When an object with CAPACITY_DOUBLING_MAX or fewer elements needs to
      * grow, double its capacity, to add N elements in amortized O(N) time.
      *
      * Above this limit, grow by 12.5% each time. Speed is still amortized
      * O(N), with a higher constant factor, and we waste less space.
      */
     static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
     static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value);
 
     uint32_t oldcap = getDenseCapacity();
     JS_ASSERT(oldcap <= newcap);
 
-    size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
-
     uint32_t nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
                       ? oldcap * 2
                       : oldcap + (oldcap >> 3);
 
     uint32_t actualCapacity = Max(newcap, nextsize);
     if (actualCapacity >= CAPACITY_CHUNK)
         actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK);
     else if (actualCapacity < SLOT_CAPACITY_MIN)
         actualCapacity = SLOT_CAPACITY_MIN;
 
     /* Don't let nelements get close to wrapping around uint32_t. */
     if (actualCapacity >= NELEMENTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap) {
-        JS_ReportOutOfMemory(cx);
         return false;
     }
 
     uint32_t initlen = getDenseInitializedLength();
     uint32_t newAllocated = actualCapacity + ObjectElements::VALUES_PER_HEADER;
 
     ObjectElements *newheader;
     if (hasDynamicElements()) {
         uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER;
         newheader = (ObjectElements *)
-            cx->realloc_(getElementsHeader(), oldAllocated * sizeof(Value),
-                         newAllocated * sizeof(Value));
+            alloc->realloc_(getElementsHeader(), oldAllocated * sizeof(Value),
+                            newAllocated * sizeof(Value));
         if (!newheader)
             return false;  /* Leave elements as its old size. */
     } else {
-        newheader = (ObjectElements *) cx->malloc_(newAllocated * sizeof(Value));
+        newheader = (ObjectElements *) alloc->malloc_(newAllocated * sizeof(Value));
         if (!newheader)
             return false;  /* Ditto. */
         js_memcpy(newheader, getElementsHeader(),
                   (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
     }
 
     newheader->capacity = actualCapacity;
     elements = newheader->elements();
 
     Debug_SetSlotRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen);
 
-    if (Probes::objectResizeActive())
-        Probes::resizeObject(cx, this, oldSize, computedSizeOfThisSlotsElements());
-
     return true;
 }
 
 void
 JSObject::shrinkElements(JSContext *cx, unsigned newcap)
 {
     uint32_t oldcap = getDenseCapacity();
     JS_ASSERT(newcap <= oldcap);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -575,16 +575,17 @@ class JSObject : public js::ObjectImpl
     static inline bool isFrozen(JSContext *cx, js::HandleObject obj, bool *resultp) {
         return isSealedOrFrozen(cx, obj, FREEZE, resultp);
     }
 
     /* Accessors for elements. */
 
     inline bool ensureElements(JSContext *cx, unsigned cap);
     bool growElements(JSContext *cx, unsigned cap);
+    bool growElements(js::Allocator *alloc, unsigned cap);
     void shrinkElements(JSContext *cx, unsigned cap);
     inline void setDynamicElements(js::ObjectElements *header);
 
     inline uint32_t getDenseCapacity();
     inline void setDenseInitializedLength(uint32_t length);
     inline void ensureDenseInitializedLength(JSContext *cx, unsigned index, unsigned extra);
     inline void setDenseElement(unsigned idx, const js::Value &val);
     inline void initDenseElement(unsigned idx, const js::Value &val);
@@ -607,16 +608,20 @@ class JSObject : public js::ObjectImpl
      * ensureDenseElements ensures that the object can hold at least
      * index + extra elements. It returns ED_OK on success, ED_FAILED on
      * failure to grow the array, ED_SPARSE when the object is too sparse to
      * grow (this includes the case of index + extra overflow). In the last
      * two cases the object is kept intact.
      */
     enum EnsureDenseResult { ED_OK, ED_FAILED, ED_SPARSE };
     inline EnsureDenseResult ensureDenseElements(JSContext *cx, unsigned index, unsigned extra);
+    inline EnsureDenseResult parExtendDenseElements(js::Allocator *alloc, js::Value *v,
+                                                    uint32_t extra);
+    template<typename CONTEXT>
+    inline EnsureDenseResult extendDenseElements(CONTEXT *cx, unsigned requiredCapacity, unsigned extra);
 
     /* Convert a single dense element to a sparse property. */
     static bool sparsifyDenseElement(JSContext *cx, js::HandleObject obj, unsigned index);
 
     /* Convert all dense elements to sparse properties. */
     static bool sparsifyDenseElements(JSContext *cx, js::HandleObject obj);
 
     /*
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -582,16 +582,90 @@ JSObject::ensureDenseInitializedLength(J
         for (js::HeapSlot *sp = elements + initlen;
              sp != elements + (index + extra);
              sp++, offset++)
             sp->init(comp, this, js::HeapSlot::Element, offset, js::MagicValue(JS_ELEMENTS_HOLE));
         initlen = index + extra;
     }
 }
 
+template<typename CONTEXT>
+JSObject::EnsureDenseResult
+JSObject::extendDenseElements(CONTEXT *cx, unsigned requiredCapacity, unsigned extra)
+{
+    /*
+     * Don't grow elements for non-extensible objects or watched objects. Dense
+     * elements can be added/written with no extensible or watchpoint checks as
+     * long as there is capacity for them.
+     */
+    if (!isExtensible() || watched()) {
+        JS_ASSERT(getDenseCapacity() == 0);
+        return ED_SPARSE;
+    }
+
+    /*
+     * Don't grow elements for objects which already have sparse indexes.
+     * This avoids needing to count non-hole elements in willBeSparseElements
+     * every time a new index is added.
+     */
+    if (isIndexed())
+        return ED_SPARSE;
+
+    /*
+     * We use the extra argument also as a hint about number of non-hole
+     * elements to be inserted.
+     */
+    if (requiredCapacity > MIN_SPARSE_INDEX &&
+        willBeSparseElements(requiredCapacity, extra)) {
+        return ED_SPARSE;
+    }
+
+    if (!growElements(cx, requiredCapacity))
+        return ED_FAILED;
+
+    return ED_OK;
+}
+
+inline JSObject::EnsureDenseResult
+JSObject::parExtendDenseElements(js::Allocator *alloc, js::Value *v, uint32_t extra)
+{
+    JS_ASSERT(isNative());
+
+    js::ObjectElements *header = getElementsHeader();
+    unsigned initializedLength = header->initializedLength;
+    unsigned requiredCapacity = initializedLength + extra;
+    if (requiredCapacity < initializedLength)
+        return ED_SPARSE; /* Overflow. */
+
+    if (requiredCapacity > header->capacity) {
+        EnsureDenseResult edr = extendDenseElements(alloc, requiredCapacity, extra);
+        if (edr != ED_OK)
+            return edr;
+    }
+
+    // Watch out lest the header has been reallocated by
+    // extendDenseElements():
+    header = getElementsHeader();
+
+    js::HeapSlot *sp = elements + initializedLength;
+    if (v) {
+        for (uint32_t i = 0; i < extra; i++)
+            sp[i].init(compartment(), this, js::HeapSlot::Element,
+                       initializedLength+i, v[i]);
+    } else {
+        for (uint32_t i = 0; i < extra; i++)
+            sp[i].init(compartment(), this, js::HeapSlot::Element,
+                       initializedLength+i, js::MagicValue(JS_ELEMENTS_HOLE));
+    }
+    header->initializedLength = requiredCapacity;
+    if (header->length < requiredCapacity)
+        header->length = requiredCapacity;
+    return ED_OK;
+}
+
 inline JSObject::EnsureDenseResult
 JSObject::ensureDenseElements(JSContext *cx, unsigned index, unsigned extra)
 {
     JS_ASSERT(isNative());
 
     unsigned currentCapacity = getDenseCapacity();
 
     unsigned requiredCapacity;
@@ -613,45 +687,19 @@ JSObject::ensureDenseElements(JSContext 
             return ED_SPARSE;
         }
         if (requiredCapacity <= currentCapacity) {
             ensureDenseInitializedLength(cx, index, extra);
             return ED_OK;
         }
     }
 
-    /*
-     * Don't grow elements for non-extensible objects or watched objects. Dense
-     * elements can be added/written with no extensible or watchpoint checks as
-     * long as there is capacity for them.
-     */
-    if (!isExtensible() || watched()) {
-        JS_ASSERT(currentCapacity == 0);
-        return ED_SPARSE;
-    }
-
-    /*
-     * Don't grow elements for objects which already have sparse indexes.
-     * This avoids needing to count non-hole elements in willBeSparseElements
-     * every time a new index is added.
-     */
-    if (isIndexed())
-        return ED_SPARSE;
-
-    /*
-     * We use the extra argument also as a hint about number of non-hole
-     * elements to be inserted.
-     */
-    if (requiredCapacity > MIN_SPARSE_INDEX &&
-        willBeSparseElements(requiredCapacity, extra)) {
-        return ED_SPARSE;
-    }
-
-    if (!growElements(cx, requiredCapacity))
-        return ED_FAILED;
+    EnsureDenseResult edr = extendDenseElements(cx, requiredCapacity, extra);
+    if (edr != ED_OK)
+        return edr;
 
     ensureDenseInitializedLength(cx, index, extra);
     return ED_OK;
 }
 
 namespace js {
 
 /*
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -208,16 +208,18 @@ typedef struct PRCallOnceType    JSCallO
 typedef JSBool                   JSCallOnceType;
 #endif
 typedef JSBool                 (*JSInitCallback)(void);
 
 #ifdef __cplusplus
 
 namespace js {
 
+class Allocator;
+
 template <typename T>
 class Rooted;
 
 class SkipRoot;
 
 enum ThingRootKind
 {
     THING_ROOT_OBJECT,
@@ -297,55 +299,80 @@ struct ContextFriendFields {
 
 struct RuntimeFriendFields {
     /*
      * If non-zero, we were been asked to call the operation callback as soon
      * as possible.
      */
     volatile int32_t    interrupt;
 
-    /* Limit pointer for checking native stack consumption. */
-    uintptr_t           nativeStackLimit;
-
     RuntimeFriendFields()
-      : interrupt(0),
-        nativeStackLimit(0) { }
+      : interrupt(0) { }
 
     static const RuntimeFriendFields *get(const JSRuntime *rt) {
         return reinterpret_cast<const RuntimeFriendFields *>(rt);
     }
 };
 
 class PerThreadData;
 
 struct PerThreadDataFriendFields
 {
+  private:
+    // Note: this type only exists to permit us to derive the offset of
+    // the perThread data within the real JSRuntime* type in a portable
+    // way.
+    struct RuntimeDummy : RuntimeFriendFields
+    {
+        struct PerThreadDummy {
+            void *field1;
+            uintptr_t field2;
+#ifdef DEBUG
+            uint64_t field3;
+#endif
+        } mainThread;
+    };
+
+  public:
+
     PerThreadDataFriendFields();
 
 #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
     /*
      * Stack allocated list of stack locations which hold non-relocatable
      * GC heap pointers (where the target is rooted somewhere else) or integer
      * values which may be confused for GC heap pointers. These are used to
      * suppress false positives which occur when a rooting analysis treats the
      * location as holding a relocatable pointer, but have no other effect on
      * GC behavior.
      */
     SkipRoot *skipGCRooters;
 #endif
 
-    static PerThreadDataFriendFields *get(js::PerThreadData *pt) {
+    /* Limit pointer for checking native stack consumption. */
+    uintptr_t nativeStackLimit;
+
+    static const size_t RuntimeMainThreadOffset = offsetof(RuntimeDummy, mainThread);
+
+    static inline PerThreadDataFriendFields *get(js::PerThreadData *pt) {
         return reinterpret_cast<PerThreadDataFriendFields *>(pt);
     }
 
-    static PerThreadDataFriendFields *getMainThread(JSRuntime *rt) {
+    static inline PerThreadDataFriendFields *getMainThread(JSRuntime *rt) {
         // mainThread must always appear directly after |RuntimeFriendFields|.
         // Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp|
         return reinterpret_cast<PerThreadDataFriendFields *>(
-            reinterpret_cast<char*>(rt) + sizeof(RuntimeFriendFields));
+            reinterpret_cast<char*>(rt) + RuntimeMainThreadOffset);
+    }
+
+    static inline const PerThreadDataFriendFields *getMainThread(const JSRuntime *rt) {
+        // mainThread must always appear directly after |RuntimeFriendFields|.
+        // Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp|
+        return reinterpret_cast<const PerThreadDataFriendFields *>(
+            reinterpret_cast<const char*>(rt) + RuntimeMainThreadOffset);
     }
 };
 
 } /* namespace js */
 
 #endif /* __cplusplus */
 
 #endif /* jspubtd_h___ */
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1306,17 +1306,17 @@ ParseNodeToXML(Parser *parser, ParseNode
     JSLinearString *str;
     uint32_t length, n, i, j;
     ParseNode *pn2, *pn3, *head, **pnp;
     JSObject *ns;
     JSObject *qn, *attrjqn;
     JSXMLClass xml_class;
     int stackDummy;
 
-    if (!JS_CHECK_STACK_SIZE(cx->runtime->nativeStackLimit, &stackDummy)) {
+    if (!JS_CHECK_STACK_SIZE(cx->mainThread().nativeStackLimit, &stackDummy)) {
         parser->reportError(pn, JSMSG_OVER_RECURSED);
         return NULL;
     }
 
 #define PN2X_SKIP_CHILD ((JSXML *) 1)
 
     /*
      * Cases return early to avoid common code that gets an outermost xml's
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -1359,17 +1359,17 @@ static const JSC::MacroAssembler::Regist
             return jump();
 #endif
 
         /*
          * Inline FreeSpan::allocate. Only the case where the current freelist
          * span is not empty is handled.
          */
         gc::FreeSpan *list = const_cast<gc::FreeSpan *>
-                             (cx->compartment->arenas.getFreeList(allocKind));
+                             (cx->compartment->allocator.arenas.getFreeList(allocKind));
         loadPtr(&list->first, result);
 
         Jump jump = branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(&list->last), result);
 
         addPtr(Imm32(thingSize), result);
         storePtr(result, &list->first);
 
         /*
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -617,17 +617,17 @@ class CallCompiler : public BaseCompiler
         masm.move(ImmPtr(f.regs.pc), t1);
 
         /* Set the CALLING_INTO_ION flag on the existing frame. */
         masm.load32(Address(JSFrameReg, StackFrame::offsetOfFlags()), t0);
         masm.or32(Imm32(StackFrame::CALLING_INTO_ION), t0);
         masm.store32(t0, Address(JSFrameReg, StackFrame::offsetOfFlags()));
 
         /* Store the entry fp and calling pc into the IonActivation. */
-        masm.loadPtr(&cx->runtime->ionActivation, t0);
+        masm.loadPtr(&cx->mainThread().ionActivation, t0);
         masm.storePtr(JSFrameReg, Address(t0, ion::IonActivation::offsetOfEntryFp()));
         masm.storePtr(t1, Address(t0, ion::IonActivation::offsetOfPrevPc()));
 
         /* Store critical fields in the VMFrame. This clobbers |t1|. */
         int32_t storeFrameDepth = ic.frameSize.staticLocalSlots();
         masm.storePtr(ImmPtr((void *)ic.frameSize.rejoinState(f.pc(), true)),
                       FrameAddress(offsetof(VMFrame, stubRejoin)));
         masm.setupFallibleABICall(cx->typeInferenceEnabled(), f.regs.pc, storeFrameDepth);
@@ -790,17 +790,17 @@ class CallCompiler : public BaseCompiler
         masm.xorPtr(JSReturnReg_Data, JSReturnReg_Type);
 #endif
 
         /* Unset VMFrame::stubRejoin. */
         masm.storePtr(ImmPtr(NULL), FrameAddress(offsetof(VMFrame, stubRejoin)));
 
         /* Unset IonActivation::entryfp. */
         t0 = regs.takeAnyReg().reg();
-        masm.loadPtr(&cx->runtime->ionActivation, t0);
+        masm.loadPtr(&cx->mainThread().ionActivation, t0);
         masm.storePtr(ImmPtr(NULL), Address(t0, ion::IonActivation::offsetOfEntryFp()));
         masm.storePtr(ImmPtr(NULL), Address(t0, ion::IonActivation::offsetOfPrevPc()));
 
         /* Unset CALLING_INTO_ION on the JS stack frame. */
         masm.load32(Address(JSFrameReg, StackFrame::offsetOfFlags()), t0);
         masm.and32(Imm32(~StackFrame::CALLING_INTO_ION), t0);
         masm.store32(t0, Address(JSFrameReg, StackFrame::offsetOfFlags()));
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3539,17 +3539,17 @@ RelaxRootChecks(JSContext *cx, unsigned 
 {
     if (argc > 0) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS,
                              "relaxRootChecks");
         return false;
     }
 
 #ifdef DEBUG
-    cx->runtime->mainThread.gcRelaxRootChecks = true;
+    cx->mainThread().gcRelaxRootChecks = true;
 #endif
 
     return true;
 }
 
 static JSBool
 GetMaxArgs(JSContext *cx, unsigned arg, jsval *vp)
 {
--- a/js/src/vm/CharacterEncoding.cpp
+++ b/js/src/vm/CharacterEncoding.cpp
@@ -4,16 +4,18 @@
  * 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 "jscntxt.h"
 
 #include "js/CharacterEncoding.h"
 
+#include "jscntxtinlines.h"
+
 using namespace JS;
 
 Latin1CharsZ
 JS::LossyTwoByteCharsToNewLatin1CharsZ(JSContext *cx, TwoByteChars tbchars)
 {
     AutoAssertNoGC nogc;
     JS_ASSERT(cx);
     size_t len = tbchars.length();
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -110,16 +110,17 @@
     macro(NaN, NaN, "NaN") \
     macro(next, next, "next") \
     macro(noSuchMethod, noSuchMethod, "__noSuchMethod__") \
     macro(NumberFormat, NumberFormat, "NumberFormat") \
     macro(NumberFormatFormat, NumberFormatFormat, "Intl_NumberFormat_format") \
     macro(objectNull, objectNull, "[object Null]") \
     macro(objectUndefined, objectUndefined, "[object Undefined]") \
     macro(of, of, "of") \
+    macro(offset, offset, "offset") \
     macro(parseFloat, parseFloat, "parseFloat") \
     macro(parseInt, parseInt, "parseInt") \
     macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
     macro(proto, proto, "__proto__") \
     macro(return, return_, "return") \
     macro(set, set, "set") \
     macro(shape, shape, "shape") \
     macro(source, source, "source") \
deleted file mode 100644
--- a/js/src/vm/ForkJoin-inl.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sw=4 et tw=78:
- *
- * 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_inl_h__
-#define ForkJoin_inl_h__
-
-namespace js {
-
-inline ForkJoinSlice *
-ForkJoinSlice::current()
-{
-#ifdef JS_THREADSAFE_ION
-    return (ForkJoinSlice*) PR_GetThreadPrivate(ThreadPrivateIndex);
-#else
-    return NULL;
-#endif
-}
-
-// True if this thread is currently executing a parallel operation across
-// multiple threads.
-static inline bool
-InParallelSection()
-{
-#ifdef JS_THREADSAFE
-    return ForkJoinSlice::current() != NULL;
-#else
-    return false;
-#endif
-}
-
-} // namespace js
-
-#endif // ForkJoin_inl_h__
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -5,18 +5,19 @@
  * 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 "jscntxt.h"
 #include "jscompartment.h"
 
 #include "vm/ForkJoin.h"
 #include "vm/Monitor.h"
+#include "gc/Marking.h"
 
-#include "vm/ForkJoin-inl.h"
+#include "jsinferinlines.h"
 
 #ifdef JS_THREADSAFE
 #  include "prthread.h"
 #endif
 
 using namespace js;
 
 #ifdef JS_THREADSAFE
@@ -24,34 +25,37 @@ using namespace js;
 class js::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.
-    const size_t numThreads_;      // Total number of threads.
+    const uint32_t numSlices_;     // Total number of threads.
     PRCondVar *rendezvousEnd_;     // Cond. var used to signal end of rendezvous.
 
     /////////////////////////////////////////////////////////////////////////
     // Per-thread arenas
     //
     // Each worker thread gets an arena to use when allocating.
 
-    Vector<gc::ArenaLists *, 16> arenaListss_;
+    Vector<Allocator *, 16> allocators_;
 
     /////////////////////////////////////////////////////////////////////////
     // Locked Fields
     //
     // Only to be accessed while holding the lock.
 
-    size_t uncompleted_;     // Number of uncompleted worker threads.
-    size_t blocked_;         // Number of threads that have joined the rendezvous.
-    size_t rendezvousIndex_; // Number of rendezvous attempts
+    uint32_t uncompleted_;         // Number of uncompleted worker threads
+    uint32_t blocked_;             // Number of threads that have joined rendezvous
+    uint32_t rendezvousIndex_;     // Number of rendezvous attempts
+    bool gcRequested_;             // True if a worker requested a GC
+    gcreason::Reason gcReason_;    // Reason given to request GC
+    JSCompartment *gcCompartment_; // Compartment for GC, or NULL for full
 
     /////////////////////////////////////////////////////////////////////////
     // Asynchronous Flags
     //
     // These can be read without the lock (hence the |volatile| declaration).
 
     // A thread has bailed and others should follow suit.  Set and read
     // asynchronously.  After setting abort, workers will acquire the lock,
@@ -63,21 +67,21 @@ class js::ForkJoinShared : public TaskEx
     volatile bool fatal_;
 
     // A thread has request a rendezvous.  Only *written* with the lock (in
     // |initiateRendezvous()| and |endRendezvous()|) but may be *read* without
     // the lock.
     volatile bool rendezvous_;
 
     // Invoked only from the main thread:
-    void executeFromMainThread(uintptr_t stackLimit);
+    void executeFromMainThread();
 
     // Executes slice #threadId of the work, either from a worker or
     // the main thread.
-    void executePortion(PerThreadData *perThread, size_t threadId, uintptr_t stackLimit);
+    void executePortion(PerThreadData *perThread, uint32_t threadId);
 
     // Rendezvous protocol:
     //
     // Use AutoRendezvous rather than invoking initiateRendezvous() and
     // endRendezvous() directly.
 
     friend class AutoRendezvous;
 
@@ -92,38 +96,46 @@ 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,
-                   size_t numThreads,
-                   size_t uncompleted);
+                   uint32_t numSlices,
+                   uint32_t uncompleted);
     ~ForkJoinShared();
 
     bool init();
 
     ParallelResult execute();
 
     // Invoked from parallel worker threads:
-    virtual void executeFromWorker(size_t threadId, uintptr_t stackLimit);
+    virtual void executeFromWorker(uint32_t threadId, uintptr_t stackLimit);
 
-    // Moves all the per-thread arenas into the main compartment.  This can
-    // only safely be invoked on the main thread, either during a rendezvous
-    // or after the workers have completed.
-    void transferArenasToCompartment();
+    // Moves all the per-thread arenas into the main compartment and
+    // processes any pending requests for a GC.  This can only safely
+    // be invoked on the main thread, either during a rendezvous or
+    // after the workers have completed.
+    void transferArenasToCompartmentAndProcessGCRequests();
 
     // Invoked during processing by worker threads to "check in".
     bool check(ForkJoinSlice &threadCx);
 
     // See comment on |ForkJoinSlice::setFatal()| in forkjoin.h
     bool setFatal();
 
+    // Requests a GC, either full or specific to a compartment.
+    void requestGC(gcreason::Reason reason);
+    void requestCompartmentGC(JSCompartment *compartment, gcreason::Reason reason);
+
+    // Requests that computation abort.
+    void setAbortFlag();
+
     JSRuntime *runtime() { return cx_->runtime; }
 };
 
 class js::AutoRendezvous
 {
   private:
     ForkJoinSlice &threadCx;
 
@@ -155,188 +167,201 @@ class js::AutoSetForkJoinSlice
 
 /////////////////////////////////////////////////////////////////////////////
 // ForkJoinShared
 //
 
 ForkJoinShared::ForkJoinShared(JSContext *cx,
                                ThreadPool *threadPool,
                                ForkJoinOp &op,
-                               size_t numThreads,
-                               size_t uncompleted)
-    : cx_(cx),
-      threadPool_(threadPool),
-      op_(op),
-      numThreads_(numThreads),
-      arenaListss_(cx),
-      uncompleted_(uncompleted),
-      blocked_(0),
-      rendezvousIndex_(0),
-      abort_(false),
-      fatal_(false),
-      rendezvous_(false)
+                               uint32_t numSlices,
+                               uint32_t uncompleted)
+  : cx_(cx),
+    threadPool_(threadPool),
+    op_(op),
+    numSlices_(numSlices),
+    allocators_(cx),
+    uncompleted_(uncompleted),
+    blocked_(0),
+    rendezvousIndex_(0),
+    gcRequested_(false),
+    gcReason_(gcreason::NUM_REASONS),
+    gcCompartment_(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
-    // compartment ArenaLists for the main thread.  This is not true,
+    // compartment |Allocator| for the main thread.  This is not true,
     // because when executing parallel code we sometimes check what
     // arena list an object is in to decide if it is writable.  If we
-    // used the compartment ArenaLists for the main thread, then the
+    // used the compartment |Allocator| for the main thread, then the
     // main thread would be permitted to write to any object it wants.
 
     if (!Monitor::init())
         return false;
 
     rendezvousEnd_ = PR_NewCondVar(lock_);
     if (!rendezvousEnd_)
         return false;
 
-    for (unsigned i = 0; i < numThreads_; i++) {
-        gc::ArenaLists *arenaLists = cx_->new_<gc::ArenaLists>();
-        if (!arenaLists)
+    for (unsigned i = 0; i < numSlices_; i++) {
+        Allocator *allocator = cx_->runtime->new_<Allocator>(cx_->compartment);
+        if (!allocator)
             return false;
 
-        if (!arenaListss_.append(arenaLists)) {
-            delete arenaLists;
+        if (!allocators_.append(allocator)) {
+            js_delete(allocator);
             return false;
         }
     }
 
     return true;
 }
 
 ForkJoinShared::~ForkJoinShared()
 {
     PR_DestroyCondVar(rendezvousEnd_);
 
-    while (arenaListss_.length() > 0)
-        delete arenaListss_.popCopy();
+    while (allocators_.length() > 0)
+        js_delete(allocators_.popCopy());
 }
 
 ParallelResult
 ForkJoinShared::execute()
 {
-    AutoLockMonitor lock(*this);
+    // Sometimes a GC request occurs *just before* we enter into the
+    // parallel section.  Rather than enter into the parallel section
+    // and then abort, we just check here and abort early.
+    if (cx_->runtime->interrupt)
+        return TP_RETRY_SEQUENTIALLY;
 
-    // Give the task set a chance to prepare for parallel workload.
-    if (!op_.pre(numThreads_))
-        return TP_RETRY_SEQUENTIALLY;
+    AutoLockMonitor lock(*this);
 
     // Notify workers to start and execute one portion on this thread.
     {
         AutoUnlockMonitor unlock(*this);
         if (!threadPool_->submitAll(cx_, this))
             return TP_FATAL;
-        executeFromMainThread(cx_->runtime->ionStackLimit);
+        executeFromMainThread();
     }
 
     // Wait for workers to complete.
     while (uncompleted_ > 0)
         lock.wait();
 
+    transferArenasToCompartmentAndProcessGCRequests();
+
     // Check if any of the workers failed.
     if (abort_) {
         if (fatal_)
             return TP_FATAL;
         else
             return TP_RETRY_SEQUENTIALLY;
     }
 
-    transferArenasToCompartment();
-
-    // Give task set a chance to cleanup after parallel execution.
-    if (!op_.post(numThreads_))
-        return TP_RETRY_SEQUENTIALLY;
-
     // Everything went swimmingly. Give yourself a pat on the back.
     return TP_SUCCESS;
 }
 
 void
-ForkJoinShared::transferArenasToCompartment()
+ForkJoinShared::transferArenasToCompartmentAndProcessGCRequests()
 {
-#if 0
-    // XXX: This code will become relevant once other bugs are merged down.
+    JSCompartment *comp = cx_->compartment;
+    for (unsigned i = 0; i < numSlices_; i++)
+        comp->adoptWorkerAllocator(allocators_[i]);
 
-    JSRuntime *rt = cx_->runtime;
-    JSCompartment *comp = cx_->compartment;
-    for (unsigned i = 0; i < numThreads_; i++)
-        comp->arenas.adoptArenas(rt, arenaListss_[i]);
-#endif
+    if (gcRequested_) {
+        if (!gcCompartment_)
+            TriggerGC(cx_->runtime, gcReason_);
+        else
+            TriggerCompartmentGC(gcCompartment_, gcReason_);
+        gcRequested_ = false;
+        gcCompartment_ = NULL;
+    }
 }
 
 void
-ForkJoinShared::executeFromWorker(size_t workerId, uintptr_t stackLimit)
+ForkJoinShared::executeFromWorker(uint32_t workerId, uintptr_t stackLimit)
 {
-    JS_ASSERT(workerId < numThreads_ - 1);
+    JS_ASSERT(workerId < numSlices_ - 1);
 
     PerThreadData thisThread(cx_->runtime);
     TlsPerThreadData.set(&thisThread);
-    executePortion(&thisThread, workerId, stackLimit);
+    thisThread.ionStackLimit = stackLimit;
+    executePortion(&thisThread, workerId);
     TlsPerThreadData.set(NULL);
 
     AutoLockMonitor lock(*this);
     uncompleted_ -= 1;
     if (blocked_ == uncompleted_) {
         // Signal the main thread that we have terminated.  It will be either
         // working, arranging a rendezvous, or waiting for workers to
         // complete.
         lock.notify();
     }
 }
 
 void
-ForkJoinShared::executeFromMainThread(uintptr_t stackLimit)
+ForkJoinShared::executeFromMainThread()
 {
-    executePortion(&cx_->runtime->mainThread, numThreads_ - 1, stackLimit);
+    executePortion(&cx_->runtime->mainThread, numSlices_ - 1);
 }
 
 void
 ForkJoinShared::executePortion(PerThreadData *perThread,
-                               size_t threadId,
-                               uintptr_t stackLimit)
+                               uint32_t threadId)
 {
-    gc::ArenaLists *arenaLists = arenaListss_[threadId];
-    ForkJoinSlice slice(perThread, threadId, numThreads_,
-                        stackLimit, arenaLists, this);
+    Allocator *allocator = allocators_[threadId];
+    ForkJoinSlice slice(perThread, threadId, numSlices_, allocator, this);
     AutoSetForkJoinSlice autoContext(&slice);
 
     if (!op_.parallel(slice))
-        abort_ = true;
+        setAbortFlag();
 }
 
 bool
 ForkJoinShared::setFatal()
 {
     // Might as well set the abort flag to true, as it will make propagation
     // faster.
-    abort_ = true;
+    setAbortFlag();
     fatal_ = true;
     return false;
 }
 
 bool
 ForkJoinShared::check(ForkJoinSlice &slice)
 {
     if (abort_)
         return false;
 
     if (slice.isMainThread()) {
+        JS_ASSERT(!cx_->runtime->gcIsNeeded);
+
         if (cx_->runtime->interrupt) {
+            // The GC Needed flag should not be set during parallel
+            // execution.  Instead, one of the requestGC() or
+            // requestCompartmentGC() methods should be invoked.
+            JS_ASSERT(!cx_->runtime->gcIsNeeded);
+
             // If interrupt is requested, bring worker threads to a halt,
             // service the interrupt, then let them start back up again.
-            AutoRendezvous autoRendezvous(slice);
-            if (!js_HandleExecutionInterrupt(cx_))
-                return setFatal();
+            // AutoRendezvous autoRendezvous(slice);
+            // if (!js_HandleExecutionInterrupt(cx_))
+            //     return setFatal();
+            setAbortFlag();
+            return false;
         }
     } else if (rendezvous_) {
         joinRendezvous(slice);
     }
 
     return true;
 }
 
@@ -387,17 +412,17 @@ ForkJoinShared::initiateRendezvous(ForkJ
 
 void
 ForkJoinShared::joinRendezvous(ForkJoinSlice &slice)
 {
     JS_ASSERT(!slice.isMainThread());
     JS_ASSERT(rendezvous_);
 
     AutoLockMonitor lock(*this);
-    const size_t index = rendezvousIndex_;
+    const uint32_t index = rendezvousIndex_;
     blocked_ += 1;
 
     // If we're the last to arrive, let the main thread know about it.
     if (blocked_ == uncompleted_)
         lock.notify();
 
     // Wait until the main thread terminates the rendezvous.  We use a
     // separate condition variable here to distinguish between workers
@@ -416,31 +441,66 @@ ForkJoinShared::endRendezvous(ForkJoinSl
     rendezvous_ = false;
     blocked_ = 0;
     rendezvousIndex_ += 1;
 
     // Signal other threads that rendezvous is over.
     PR_NotifyAllCondVar(rendezvousEnd_);
 }
 
+void
+ForkJoinShared::setAbortFlag()
+{
+    abort_ = true;
+}
+
+void
+ForkJoinShared::requestGC(gcreason::Reason reason)
+{
+    AutoLockMonitor lock(*this);
+
+    gcCompartment_ = NULL;
+    gcReason_ = reason;
+    gcRequested_ = true;
+}
+
+void
+ForkJoinShared::requestCompartmentGC(JSCompartment *compartment,
+                                     gcreason::Reason reason)
+{
+    AutoLockMonitor lock(*this);
+
+    if (gcRequested_ && gcCompartment_ != compartment) {
+        // If a full GC has been requested, or a GC for another compartment,
+        // issue a request for a full GC.
+        gcCompartment_ = NULL;
+        gcReason_ = reason;
+        gcRequested_ = true;
+    } else {
+        // Otherwise, just GC this compartment.
+        gcCompartment_ = compartment;
+        gcReason_ = reason;
+        gcRequested_ = true;
+    }
+}
+
 #endif // JS_THREADSAFE
 
 /////////////////////////////////////////////////////////////////////////////
 // ForkJoinSlice
 //
 
 ForkJoinSlice::ForkJoinSlice(PerThreadData *perThreadData,
-                             size_t sliceId, size_t numSlices,
-                             uintptr_t stackLimit, gc::ArenaLists *arenaLists,
-                             ForkJoinShared *shared)
+                             uint32_t sliceId, uint32_t numSlices,
+                             Allocator *allocator, ForkJoinShared *shared)
     : perThreadData(perThreadData),
       sliceId(sliceId),
       numSlices(numSlices),
-      ionStackLimit(stackLimit),
-      arenaLists(arenaLists),
+      allocator(allocator),
+      abortedScript(NULL),
       shared(shared)
 { }
 
 bool
 ForkJoinSlice::isMainThread()
 {
 #ifdef JS_THREADSAFE
     return perThreadData == &shared->runtime()->mainThread;
@@ -485,30 +545,110 @@ ForkJoinSlice::Initialize()
 #ifdef JS_THREADSAFE
     PRStatus status = PR_NewThreadPrivateIndex(&ThreadPrivateIndex, NULL);
     return status == PR_SUCCESS;
 #else
     return true;
 #endif
 }
 
+void
+ForkJoinSlice::requestGC(gcreason::Reason reason)
+{
+#ifdef JS_THREADSAFE
+    shared->requestGC(reason);
+    triggerAbort();
+#endif
+}
+
+void
+ForkJoinSlice::requestCompartmentGC(JSCompartment *compartment,
+                                    gcreason::Reason reason)
+{
+#ifdef JS_THREADSAFE
+    shared->requestCompartmentGC(compartment, reason);
+    triggerAbort();
+#endif
+}
+
+#ifdef JS_THREADSAFE
+void
+ForkJoinSlice::triggerAbort()
+{
+    shared->setAbortFlag();
+
+    // 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.
+    perThreadData->ionStackLimit = -1;
+}
+#endif
+
 /////////////////////////////////////////////////////////////////////////////
 
+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 (IsIncrementalGCInProgress(cx->runtime)) {
+            PrepareForIncrementalGC(cx->runtime);
+            FinishIncrementalGC(cx->runtime, gcreason::API);
+        }
+
+        cx->runtime->gcHelperThread.waitBackgroundSweepEnd();
+    }
+
+    ~AutoEnterParallelSection() {
+        cx_->runtime->mainThread.ionTop = prevIonTop_;
+    }
+};
+} /* namespace js */
+
+uint32_t
+js::ForkJoinSlices(JSContext *cx)
+{
+#ifndef JS_THREADSAFE
+    return 1;
+#else
+    // Parallel workers plus this main thread.
+    return cx->runtime->threadPool.numWorkers() + 1;
+#endif
+}
+
 ParallelResult
 js::ExecuteForkJoinOp(JSContext *cx, ForkJoinOp &op)
 {
 #ifdef JS_THREADSAFE
     // Recursive use of the ThreadPool is not supported.
-    JS_ASSERT(!InParallelSection());
+    JS_ASSERT(!ForkJoinSlice::InParallelSection());
+
+    AutoEnterParallelSection enter(cx);
 
     ThreadPool *threadPool = &cx->runtime->threadPool;
-    // Parallel workers plus this main thread.
-    size_t numThreads = threadPool->numWorkers() + 1;
+    uint32_t numSlices = ForkJoinSlices(cx);
 
-    ForkJoinShared shared(cx, threadPool, op, numThreads, numThreads - 1);
+    ForkJoinShared shared(cx, threadPool, op, numSlices, numSlices - 1);
     if (!shared.init())
         return TP_RETRY_SEQUENTIALLY;
 
     return shared.execute();
 #else
     return TP_RETRY_SEQUENTIALLY;
 #endif
 }
--- a/js/src/vm/ForkJoin.h
+++ b/js/src/vm/ForkJoin.h
@@ -21,38 +21,29 @@
 // invoke |ExecuteForkJoinOp()|, as follows:
 //
 //     class MyForkJoinOp {
 //       ... define callbacks as appropriate for your operation ...
 //     };
 //     MyForkJoinOp op;
 //     ExecuteForkJoinOp(cx, op);
 //
-// |ExecuteForkJoinOp()| will fire up the workers in the runtime's thread
-// pool, have them execute the callbacks defined in the |ForkJoinOp| class,
-// and then return once all the workers have completed.
-//
-// There are three callbacks defined in |ForkJoinOp|.  The first, |pre()|, is
-// invoked before the parallel section begins.  It informs you how many slices
-// your problem will be divided into (effectively, how many worker threads
-// there will be).  This is often useful for allocating an array for the
-// workers to store their result or something like that.
-//
-// Next, you will receive |N| calls to the |parallel()| callback, where |N| is
-// the number of slices that were specified in |pre()|.  Each callback will be
-// supplied with a |ForkJoinSlice| instance providing some context.
+// |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.
 //
 // 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.
 //
-// Finally, after the operation is complete the |post()| callback is invoked,
-// giving you a chance to collect the various results.
-//
 // 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
@@ -64,16 +55,45 @@
 // It is assumed that anyone using this API must be prepared for a sequential
 // fallback.  Therefore, the |ExecuteForkJoinOp()| returns a status code
 // indicating whether a fatal error occurred (in which case you should just
 // stop) or whether you should retry the operation, but executing
 // sequentially.  An example of where the fallback would be useful is if the
 // parallel code encountered an unexpected path that cannot safely be executed
 // in parallel (writes to shared state, say).
 //
+// Garbage collection and allocation:
+//
+// Code which executes on these parallel threads must be very careful
+// with respect to garbage collection and allocation.  Currently, we
+// do not permit GC to occur when executing in parallel.  Furthermore,
+// the typical allocation paths are UNSAFE in parallel code because
+// they access shared state (the compartment's arena lists and so
+// forth) without any synchronization.
+//
+// To deal with this, the forkjoin code creates a distinct |Allocator|
+// object for each slice.  You can access the appropriate object via
+// the |ForkJoinSlice| object that is provided to the callbacks.  Once
+// the execution is complete, all the objects found in these distinct
+// |Allocator| is merged back into the main compartment lists and
+// things proceed normally.
+//
+// In Ion-generated code, we will do allocation through the
+// |Allocator| found in |ForkJoinSlice| (which is obtained via TLS).
+// Also, no write barriers are emitted.  Conceptually, we should never
+// need a write barrier because we only permit writes to objects that
+// are newly allocated, and such objects are always black (to use
+// incremental GC terminology).  However, to be safe, we also block
+// upon entering a parallel section to ensure that any concurrent
+// marking or incremental GC has completed.
+//
+// In the future, it should be possible to lift the restriction that
+// we must block until inc. GC has completed and also to permit GC
+// during parallel exeution. But we're not there yet.
+//
 // Current Limitations:
 //
 // - 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
@@ -83,109 +103,154 @@ 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 ForkJoinOp;
 
+// 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;
-namespace gc { struct ArenaLists; }
+
+#ifdef DEBUG
+struct IonTraceData
+{
+    uint32_t bblock;
+    uint32_t lir;
+    uint32_t execModeInt;
+    const char *lirOpName;
+    const char *mirOpName;
+    JSScript *script;
+    jsbytecode *pc;
+};
+#endif
 
 struct ForkJoinSlice
 {
   public:
     // PerThreadData corresponding to the current worker thread.
     PerThreadData *perThreadData;
 
     // Which slice should you process? Ranges from 0 to |numSlices|.
-    const size_t sliceId;
+    const uint32_t sliceId;
 
     // How many slices are there in total?
-    const size_t numSlices;
+    const uint32_t numSlices;
 
-    // Top of the stack.  This should move into |perThreadData|.
-    uintptr_t ionStackLimit;
-
-    // Arenas to use when allocating on this thread.  See
+    // Allocator to use when allocating on this thread.  See
     // |ion::ParFunctions::ParNewGCThing()|.  This should move into
     // |perThreadData|.
-    gc::ArenaLists *const arenaLists;
+    Allocator *const allocator;
+
+    // If we took a parallel bailout, the script that bailed out is stored here.
+    JSScript *abortedScript;
 
-    ForkJoinSlice(PerThreadData *perThreadData, size_t sliceId, size_t numSlices,
-                  uintptr_t stackLimit, gc::ArenaLists *arenaLists,
-                  ForkJoinShared *shared);
+    // Records the last instruction to execute on this thread.
+#ifdef DEBUG
+    IonTraceData traceData;
+#endif
+
+    ForkJoinSlice(PerThreadData *perThreadData, uint32_t sliceId, uint32_t numSlices,
+                  Allocator *arenaLists, ForkJoinShared *shared);
 
     // True if this is the main thread, false if it is one of the parallel workers.
     bool isMainThread();
 
     // Generally speaking, if a thread returns false, that is interpreted as a
     // "bailout"---meaning, a recoverable error.  If however you call this
     // function before returning false, then the error will be interpreted as
     // *fatal*.  This doesn't strike me as the most elegant solution here but
     // I don't know what'd be better.
     //
     // For convenience, *always* returns false.
     bool setFatal();
 
+    // When the code would normally trigger a GC, we don't trigger it
+    // immediately but instead record that request here.  This will
+    // cause |ExecuteForkJoinOp()| to invoke |TriggerGC()| or
+    // |TriggerCompartmentGC()| as appropriate once the parallel
+    // section is complete. This is done because those routines do
+    // various preparations that are not thread-safe, and because the
+    // full set of arenas is not available until the end of the
+    // parallel section.
+    void requestGC(gcreason::Reason reason);
+    void requestCompartmentGC(JSCompartment *compartment, gcreason::Reason reason);
+
     // During the parallel phase, this method should be invoked periodically,
     // for example on every backedge, similar to the interrupt check.  If it
     // returns false, then the parallel phase has been aborted and so you
     // should bailout.  The function may also rendesvous to perform GC or do
     // other similar things.
     bool check();
 
     // Be wary, the runtime is shared between all threads!
     JSRuntime *runtime();
 
-    static inline ForkJoinSlice *current();
+    // Check the current state of parallel execution.
+    static inline ForkJoinSlice *Current();
+    static inline bool InParallelSection();
+
     static bool Initialize();
 
   private:
     friend class AutoRendezvous;
     friend class AutoSetForkJoinSlice;
 
 #ifdef JS_THREADSAFE
     // Initialized by Initialize()
     static unsigned ThreadPrivateIndex;
 #endif
 
+#ifdef JS_THREADSAFE
+    // 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 before parallel phase begins; informs the task set how many
-    // slices there will be and gives it a chance to initialize per-slice data
-    // structures.
-    //
-    // Returns true on success, false to halt parallel execution.
-    virtual bool pre(size_t numSlices) = 0;
-
     // 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;
-
-    // Invoked after parallel phase ends if execution was successful
-    // (not aborted)
-    //
-    // Returns true on success, false to halt parallel execution.
-    virtual bool post(size_t numSlices) = 0;
 };
 
 } // namespace js
 
+/* static */ inline js::ForkJoinSlice *
+js::ForkJoinSlice::Current()
+{
+#ifdef JS_THREADSAFE
+    return (ForkJoinSlice*) PR_GetThreadPrivate(ThreadPrivateIndex);
+#else
+    return NULL;
+#endif
+}
+
+/* static */ inline bool
+js::ForkJoinSlice::InParallelSection()
+{
+    return Current() != NULL;
+}
+
 #endif // ForkJoin_h__
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -11,16 +11,17 @@
 #include "gc/Marking.h"
 #include "methodjit/MethodJIT.h"
 #ifdef JS_ION
 #include "ion/IonFrames.h"
 #include "ion/IonCompartment.h"
 #include "ion/Bailouts.h"
 #endif
 #include "Stack.h"
+#include "ForkJoin.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 #include "jsinterpinlines.h"
 
 #include "jsopcode.h"
 
 #include "Stack-inl.h"
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -353,17 +353,17 @@ JSExternalString::new_(JSContext *cx, co
     JS_ASSERT(chars[length] == 0);
 
     if (!validateLength(cx, length))
         return NULL;
     JSExternalString *str = js_NewGCExternalString(cx);
     if (!str)
         return NULL;
     str->init(chars, length, fin);
-    cx->runtime->updateMallocCounter(cx, (length + 1) * sizeof(jschar));
+    cx->runtime->updateMallocCounter(cx->compartment, (length + 1) * sizeof(jschar));
     return str;
 }
 
 inline bool
 js::StaticStrings::fitsInSmallChar(jschar c)
 {
     return c < SMALL_CHAR_LIMIT && toSmallChar[c] != INVALID_SMALL_CHAR;
 }
--- a/js/src/vm/ThreadPool.cpp
+++ b/js/src/vm/ThreadPool.cpp
@@ -113,18 +113,19 @@ ThreadPoolWorker::ThreadMain(void *arg)
     thread->run();
 }
 
 void
 ThreadPoolWorker::run()
 {
     // This is hokey in the extreme.  To compute the stack limit,
     // subtract the size of the stack from the address of a local
-    // variable and give a 2k buffer.  Is there a better way?
-    uintptr_t stackLimitOffset = WORKER_THREAD_STACK_SIZE - 2*1024;
+    // variable and give a 10k buffer.  Is there a better way?
+    // (Note: 2k proved to be fine on Mac, but too little on Linux)
+    uintptr_t stackLimitOffset = WORKER_THREAD_STACK_SIZE - 10*1024;
     uintptr_t stackLimit = (((uintptr_t)&stackLimitOffset) +
                              stackLimitOffset * JS_STACK_GROWTH_DIRECTION);
 
     AutoLockMonitor lock(*this);
 
     for (;;) {
         while (!worklist_.empty()) {
             TaskExecutor *task = worklist_.popCopy();
--- a/js/src/vm/ThreadPool.h
+++ b/js/src/vm/ThreadPool.h
@@ -23,22 +23,22 @@ struct JSContext;
 struct JSRuntime;
 struct JSCompartment;
 class JSScript;
 
 namespace js {
 
 class ThreadPoolWorker;
 
-typedef void (*TaskFun)(void *userdata, size_t workerId, uintptr_t stackLimit);
+typedef void (*TaskFun)(void *userdata, uint32_t workerId, uintptr_t stackLimit);
 
 class TaskExecutor
 {
   public:
-    virtual void executeFromWorker(size_t workerId, uintptr_t stackLimit) = 0;
+    virtual void executeFromWorker(uint32_t workerId, uintptr_t stackLimit) = 0;
 };
 
 // ThreadPool used for parallel JavaScript execution as well as
 // parallel compilation.  Unless you are building a new kind of
 // parallel service, it is very likely that you do not wish to
 // interact with the threadpool directly.  In particular, if you wish
 // to execute JavaScript in parallel, you probably want to look at
 // |js::ForkJoin| in |forkjoin.cpp|.
@@ -72,17 +72,17 @@ class ThreadPool
     // Initialized at startup only:
     JSRuntime *const runtime_;
     js::Vector<ThreadPoolWorker*, 8, SystemAllocPolicy> workers_;
 
     // Number of workers we will start, when we actually start them
     size_t numWorkers_;
 
     // Next worker for |submitOne()|. Atomically modified.
-    size_t nextId_;
+    uint32_t nextId_;
 
     bool lazyStartWorkers(JSContext *cx);
     void terminateWorkers();
     void terminateWorkersAndReportOOM(JSContext *cx);
 
   public:
     ThreadPool(JSRuntime *rt);
     ~ThreadPool();