Backed out 3 changesets (bug 1019304) for spidermonkey test failures on a CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Fri, 20 Jun 2014 13:33:03 -0700
changeset 190009 724d46a1b00a58fff7b846193c082759c4d43eac
parent 190008 8b3b7b0e9f812d834cd445bd750e89ee077c401b
child 190010 6da364bc0e3ec25e0783c25f6f186ba572cb6cf4
push id8288
push userryanvm@gmail.com
push dateMon, 23 Jun 2014 14:59:00 +0000
treeherderb2g-inbound@c65bf5a0595c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1019304
milestone33.0a1
backs outadc7e2d717a900ced212e10e57be2466d91c9b2c
5322e67211418151d6eb46cfb5c15dcdc293fe90
45f24290b96ebf3e768130bf9ac876b34cbfa217
Backed out 3 changesets (bug 1019304) for spidermonkey test failures on a CLOSED TREE Backed out changeset adc7e2d717a9 (bug 1019304) Backed out changeset 5322e6721141 (bug 1019304) Backed out changeset 45f24290b96e (bug 1019304)
js/src/gc/ForkJoinNursery.cpp
js/src/gc/ForkJoinNursery.h
js/src/jit/Bailouts.cpp
js/src/jit/Bailouts.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/Ion.cpp
js/src/jit/IonFrames.cpp
js/src/jit/IonSpewer.cpp
js/src/jit/IonSpewer.h
js/src/jit/JitCompartment.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MIRGraph.cpp
js/src/jit/MIRGraph.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelFunctions.cpp
js/src/jit/ParallelFunctions.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/ParallelSafetyAnalysis.h
js/src/jit/RematerializedFrame.cpp
js/src/jit/RematerializedFrame.h
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/CodeGenerator-arm.h
js/src/jit/arm/Trampoline-arm.cpp
js/src/jit/mips/CodeGenerator-mips.cpp
js/src/jit/mips/CodeGenerator-mips.h
js/src/jit/mips/Trampoline-mips.cpp
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jit/shared/CodeGenerator-shared.h
js/src/jit/shared/CodeGenerator-x86-shared.cpp
js/src/jit/shared/CodeGenerator-x86-shared.h
js/src/jit/x64/Trampoline-x64.cpp
js/src/jit/x86/Trampoline-x86.cpp
js/src/vm/ForkJoin.cpp
js/src/vm/ForkJoin.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/gc/ForkJoinNursery.cpp
+++ b/js/src/gc/ForkJoinNursery.cpp
@@ -10,17 +10,16 @@
 #include "gc/ForkJoinNursery-inl.h"
 
 #include "mozilla/IntegerPrintfMacros.h"
 
 #include "prmjtime.h"
 
 #include "gc/Heap.h"
 #include "jit/IonFrames.h"
-#include "jit/RematerializedFrame.h"
 #include "vm/ArrayObject.h"
 #include "vm/ForkJoin.h"
 #include "vm/TypedArrayObject.h"
 
 #include "jsgcinlines.h"
 #include "gc/Nursery-inl.h"
 #include "vm/ObjectImpl-inl.h"
 
@@ -386,17 +385,16 @@ ForkJoinNursery::MinorGCCallback(JSTrace
 
 void
 ForkJoinNursery::forwardFromRoots(ForkJoinNurseryCollectionTracer *trc)
 {
     // There should be no other roots as a result of effect-freedom.
     forwardFromUpdatable(trc);
     forwardFromStack(trc);
     forwardFromTenured(trc);
-    forwardFromRematerializedFrames(trc);
 }
 
 void
 ForkJoinNursery::forwardFromUpdatable(ForkJoinNurseryCollectionTracer *trc)
 {
     JSObject *obj = shared_->updatable();
     if (obj)
         traceObject(trc, obj);
@@ -441,23 +439,16 @@ ForkJoinNursery::forwardFromTenured(Fork
             for (ArenaCellIterUnderFinalize i(ai.get()); !i.done(); i.next())
                 objs[numObjs++] = i.get<JSObject>();
             for (size_t i=0; i < numObjs; i++)
                 traceObject(trc, objs[i]);
         }
     }
 }
 
-void
-ForkJoinNursery::forwardFromRematerializedFrames(ForkJoinNurseryCollectionTracer *trc)
-{
-    if (cx_->bailoutRecord->hasFrames())
-        jit::RematerializedFrame::MarkInVector(trc, cx_->bailoutRecord->frames());
-}
-
 /*static*/ void
 ForkJoinNursery::forwardBufferPointer(JSTracer *trc, HeapSlot **pSlotsElems)
 {
     ForkJoinNursery *nursery = static_cast<ForkJoinNurseryCollectionTracer *>(trc)->nursery_;
     HeapSlot *old = *pSlotsElems;
 
     if (!nursery->isInsideFromspace(old))
         return;
--- a/js/src/gc/ForkJoinNursery.h
+++ b/js/src/gc/ForkJoinNursery.h
@@ -231,17 +231,16 @@ class ForkJoinNursery
     // Misc GC internals.
     void pjsCollection(int op /* A combination of PJSCollectionOp bits */);
     void initNewspace();
     void flip();
     void forwardFromRoots(ForkJoinNurseryCollectionTracer *trc);
     void forwardFromUpdatable(ForkJoinNurseryCollectionTracer *trc);
     void forwardFromStack(ForkJoinNurseryCollectionTracer *trc);
     void forwardFromTenured(ForkJoinNurseryCollectionTracer *trc);
-    void forwardFromRematerializedFrames(ForkJoinNurseryCollectionTracer *trc);
     void collectToFixedPoint(ForkJoinNurseryCollectionTracer *trc);
     void freeFromspace();
     void computeNurserySizeAfterGC(size_t live, const char **msg);
 
     AllocKind getObjectAllocKind(JSObject *src);
     void *allocateInTospace(AllocKind thingKind);
     void *allocateInTospace(size_t nelem, size_t elemSize);
     MOZ_ALWAYS_INLINE bool shouldMoveObject(void **thingp);
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -64,16 +64,20 @@ IonBailoutIterator::dump() const
                 break;
             ++frames;
         }
     } else {
         JitFrameIterator::dump();
     }
 }
 
+// This address is a magic number made to cause crashes while indicating that we
+// are making an attempt to mark the stack during a bailout.
+static uint8_t * const FAKE_JIT_TOP_FOR_BAILOUT = reinterpret_cast<uint8_t *>(0xba1);
+
 uint32_t
 jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
 {
     JSContext *cx = GetJSContextFromJitCode();
     JS_ASSERT(bailoutInfo);
 
     // We don't have an exit frame.
     MOZ_ASSERT(size_t(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(IonCommonFrameLayout)) < 0x1000 &&
--- a/js/src/jit/Bailouts.h
+++ b/js/src/jit/Bailouts.h
@@ -95,20 +95,16 @@ static const BailoutId INVALID_BAILOUT_I
 static const uint32_t BAILOUT_TABLE_SIZE = 16;
 
 // Bailout return codes.
 // N.B. the relative order of these values is hard-coded into ::GenerateBailoutThunk.
 static const uint32_t BAILOUT_RETURN_OK = 0;
 static const uint32_t BAILOUT_RETURN_FATAL_ERROR = 1;
 static const uint32_t BAILOUT_RETURN_OVERRECURSED = 2;
 
-// This address is a magic number made to cause crashes while indicating that we
-// are making an attempt to mark the stack during a bailout.
-static uint8_t * const FAKE_JIT_TOP_FOR_BAILOUT = reinterpret_cast<uint8_t *>(0xba1);
-
 class JitCompartment;
 
 // BailoutStack is an architecture specific pointer to the stack, given by the
 // bailout handler.
 class BailoutStack;
 class InvalidationBailoutStack;
 
 // Must be implemented by each architecture.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1224,20 +1224,17 @@ class OutOfLineInterruptCheckImplicit : 
     { }
 
     bool accept(CodeGenerator *codegen) {
         return codegen->visitOutOfLineInterruptCheckImplicit(this);
     }
 };
 
 typedef bool (*InterruptCheckFn)(JSContext *);
-typedef bool (*InterruptCheckParFn)(ForkJoinContext *);
-static const VMFunctionsModal InterruptCheckInfo = VMFunctionsModal(
-    FunctionInfo<InterruptCheckFn>(InterruptCheck),
-    FunctionInfo<InterruptCheckParFn>(InterruptCheckPar));
+static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
 
 bool
 CodeGenerator::visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ool)
 {
 #ifdef CHECK_OSIPOINT_REGISTERS
     // This is path is entered from the patched back-edge of the loop. This
     // means that the JitAtivation flags used for checking the validity of the
     // OSI points are not reseted by the path generated by generateBody, so we
@@ -1847,17 +1844,23 @@ CodeGenerator::visitGuardThreadExclusive
     JS_ASSERT(gen->info().executionMode() == ParallelExecution);
 
     const Register tempReg = ToRegister(lir->getTempReg());
     masm.setupUnalignedABICall(2, tempReg);
     masm.passABIArg(ToRegister(lir->forkJoinContext()));
     masm.passABIArg(ToRegister(lir->object()));
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParallelWriteGuard));
 
-    return bailoutIfFalseBool(ReturnReg, lir->snapshot());
+    OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutIllegalWrite, lir);
+    if (!bail)
+        return false;
+
+    // branch to the OOL failure code if false is returned
+    masm.branchIfFalseBool(ReturnReg, bail->entry());
+    return true;
 }
 
 bool
 CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity *guard)
 {
     Register obj = ToRegister(guard->input());
 
     Assembler::Condition cond =
@@ -2375,33 +2378,37 @@ CodeGenerator::visitCallGeneric(LCallGen
     // replace the return value with the Object from CreateThis.
     if (call->mir()->isConstructing()) {
         Label notPrimitive;
         masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
         masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
         masm.bind(&notPrimitive);
     }
 
+    if (!checkForAbortPar(call))
+        return false;
+
     dropArguments(call->numStackArgs() + 1);
     return true;
 }
 
-typedef bool (*CallToUncompiledScriptParFn)(ForkJoinContext *, JSObject *);
-static const VMFunction CallToUncompiledScriptParInfo =
-    FunctionInfo<CallToUncompiledScriptParFn>(CallToUncompiledScriptPar);
-
 // Generates a call to CallToUncompiledScriptPar() and then bails out.
 // |calleeReg| should contain the JSFunction*.
 bool
 CodeGenerator::emitCallToUncompiledScriptPar(LInstruction *lir, Register calleeReg)
 {
-    pushArg(calleeReg);
-    if (!callVM(CallToUncompiledScriptParInfo, lir))
-        return false;
-    masm.assumeUnreachable("CallToUncompiledScriptParInfo always returns false.");
+    OutOfLineCode *bail = oolAbortPar(ParallelBailoutCalledToUncompiledScript, lir);
+    if (!bail)
+        return false;
+
+    masm.movePtr(calleeReg, CallTempReg0);
+    masm.setupUnalignedABICall(1, CallTempReg1);
+    masm.passABIArg(CallTempReg0);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CallToUncompiledScriptPar));
+    masm.jump(bail->entry());
     return true;
 }
 
 bool
 CodeGenerator::visitCallKnown(LCallKnown *call)
 {
     Register calleereg = ToRegister(call->getFunction());
     Register objreg    = ToRegister(call->getTempObject());
@@ -2466,30 +2473,49 @@ CodeGenerator::visitCallKnown(LCallKnown
         break;
 
       default:
         MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
     masm.bind(&end);
 
+    if (!checkForAbortPar(call))
+        return false;
+
     // If the return value of the constructing function is Primitive,
     // replace the return value with the Object from CreateThis.
     if (call->mir()->isConstructing()) {
         Label notPrimitive;
         masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
         masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
         masm.bind(&notPrimitive);
     }
 
     dropArguments(call->numStackArgs() + 1);
     return true;
 }
 
 bool
+CodeGenerator::checkForAbortPar(LInstruction *lir)
+{
+    // In parallel mode, if we call another ion-compiled function and
+    // it returns JS_ION_ERROR, that indicates a bailout that we have
+    // to propagate up the stack.
+    ExecutionMode executionMode = gen->info().executionMode();
+    if (executionMode == ParallelExecution) {
+        OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(lir);
+        if (!bail)
+            return false;
+        masm.branchTestMagic(Assembler::Equal, JSReturnOperand, bail->entry());
+    }
+    return true;
+}
+
+bool
 CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize)
 {
     Register objreg = ToRegister(apply->getTempObject());
     JS_ASSERT(objreg != extraStackSize);
 
     // Push the space used by the arguments.
     masm.movePtr(StackPointer, objreg);
     masm.Push(extraStackSize);
@@ -2891,28 +2917,28 @@ CodeGenerator::generateArgumentsChecks(b
     masm.freeStack(frameSize());
 
     return true;
 }
 
 // Out-of-line path to report over-recursed error and fail.
 class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator>
 {
-    LInstruction *lir_;
+    LCheckOverRecursed *lir_;
 
   public:
-    explicit CheckOverRecursedFailure(LInstruction *lir)
+    explicit CheckOverRecursedFailure(LCheckOverRecursed *lir)
       : lir_(lir)
     { }
 
     bool accept(CodeGenerator *codegen) {
         return codegen->visitCheckOverRecursedFailure(this);
     }
 
-    LInstruction *lir() const {
+    LCheckOverRecursed *lir() const {
         return lir_;
     }
 };
 
 bool
 CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed *lir)
 {
     // If we don't push anything on the stack, skip the check.
@@ -2972,21 +2998,19 @@ CodeGenerator::visitDefFun(LDefFun *lir)
 
     pushArg(ImmGCPtr(lir->mir()->fun()));
     pushArg(scopeChain);
     pushArg(ImmGCPtr(current->mir()->info().script()));
 
     return callVM(DefFunOperationInfo, lir);
 }
 
-typedef bool (*CheckOverRecursedFn)(JSContext *);
-typedef bool (*CheckOverRecursedParFn)(ForkJoinContext *);
-static const VMFunctionsModal CheckOverRecursedInfo = VMFunctionsModal(
-    FunctionInfo<CheckOverRecursedFn>(CheckOverRecursed),
-    FunctionInfo<CheckOverRecursedParFn>(CheckOverRecursedPar));
+typedef bool (*ReportOverRecursedFn)(JSContext *);
+static const VMFunction CheckOverRecursedInfo =
+    FunctionInfo<ReportOverRecursedFn>(CheckOverRecursed);
 
 bool
 CodeGenerator::visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool)
 {
     // The OOL path is hit if the recursion depth has been exceeded.
     // Throw an InternalError for over-recursion.
 
     // LFunctionEnvironment can appear before LCheckOverRecursed, so we have
@@ -2997,16 +3021,35 @@ CodeGenerator::visitCheckOverRecursedFai
     if (!callVM(CheckOverRecursedInfo, ool->lir()))
         return false;
 
     restoreLive(ool->lir());
     masm.jump(ool->rejoin());
     return true;
 }
 
+// Out-of-line path to report over-recursed error and fail.
+class CheckOverRecursedFailurePar : public OutOfLineCodeBase<CodeGenerator>
+{
+    LCheckOverRecursedPar *lir_;
+
+  public:
+    explicit CheckOverRecursedFailurePar(LCheckOverRecursedPar *lir)
+      : lir_(lir)
+    { }
+
+    bool accept(CodeGenerator *codegen) {
+        return codegen->visitCheckOverRecursedFailurePar(this);
+    }
+
+    LCheckOverRecursedPar *lir() const {
+        return lir_;
+    }
+};
+
 bool
 CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
 {
     // See above: unlike visitCheckOverRecursed(), this code runs in
     // parallel mode and hence uses the jitStackLimit from the current
     // thread state.  Also, we must check the interrupt flags because
     // on interrupt or abort, only the stack limit for the main thread
     // is reset, not the worker threads.  See comment in vm/ForkJoin.h
@@ -3014,41 +3057,111 @@ CodeGenerator::visitCheckOverRecursedPar
 
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg = ToRegister(lir->getTempReg());
 
     masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg);
     masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg);
 
     // Conditional forward (unlikely) branch to failure.
-    CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
+    CheckOverRecursedFailurePar *ool = new(alloc()) CheckOverRecursedFailurePar(lir);
     if (!addOutOfLineCode(ool))
         return false;
-
     masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry());
     masm.checkInterruptFlagPar(tempReg, ool->entry());
     masm.bind(ool->rejoin());
 
     return true;
 }
 
 bool
+CodeGenerator::visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool)
+{
+    OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir());
+    if (!bail)
+        return false;
+
+    // Avoid saving/restoring the temp register since we will put the
+    // ReturnReg into it below and we don't want to clobber that
+    // during PopRegsInMask():
+    LCheckOverRecursedPar *lir = ool->lir();
+    Register tempReg = ToRegister(lir->getTempReg());
+    RegisterSet saveSet(lir->safepoint()->liveRegs());
+    saveSet.takeUnchecked(tempReg);
+
+    masm.PushRegsInMask(saveSet);
+    masm.movePtr(ToRegister(lir->forkJoinContext()), CallTempReg0);
+    masm.setupUnalignedABICall(1, CallTempReg1);
+    masm.passABIArg(CallTempReg0);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CheckOverRecursedPar));
+    masm.movePtr(ReturnReg, tempReg);
+    masm.PopRegsInMask(saveSet);
+    masm.branchIfFalseBool(tempReg, bail->entry());
+    masm.jump(ool->rejoin());
+
+    return true;
+}
+
+// Out-of-line path to report over-recursed error and fail.
+class OutOfLineInterruptCheckPar : public OutOfLineCodeBase<CodeGenerator>
+{
+  public:
+    LInterruptCheckPar *const lir;
+
+    explicit OutOfLineInterruptCheckPar(LInterruptCheckPar *lir)
+      : lir(lir)
+    { }
+
+    bool accept(CodeGenerator *codegen) {
+        return codegen->visitOutOfLineInterruptCheckPar(this);
+    }
+};
+
+bool
 CodeGenerator::visitInterruptCheckPar(LInterruptCheckPar *lir)
 {
     // First check for cx->shared->interrupt_.
-    OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
-    if (!ool)
+    OutOfLineInterruptCheckPar *ool = new(alloc()) OutOfLineInterruptCheckPar(lir);
+    if (!addOutOfLineCode(ool))
         return false;
 
     Register tempReg = ToRegister(lir->getTempReg());
     masm.checkInterruptFlagPar(tempReg, ool->entry());
     masm.bind(ool->rejoin());
     return true;
 }
 
+bool
+CodeGenerator::visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool)
+{
+    OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir);
+    if (!bail)
+        return false;
+
+    // Avoid saving/restoring the temp register since we will put the
+    // ReturnReg into it below and we don't want to clobber that
+    // during PopRegsInMask():
+    LInterruptCheckPar *lir = ool->lir;
+    Register tempReg = ToRegister(lir->getTempReg());
+    RegisterSet saveSet(lir->safepoint()->liveRegs());
+    saveSet.takeUnchecked(tempReg);
+
+    masm.PushRegsInMask(saveSet);
+    masm.movePtr(ToRegister(ool->lir->forkJoinContext()), CallTempReg0);
+    masm.setupUnalignedABICall(1, CallTempReg1);
+    masm.passABIArg(CallTempReg0);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InterruptCheckPar));
+    masm.movePtr(ReturnReg, tempReg);
+    masm.PopRegsInMask(saveSet);
+    masm.branchIfFalseBool(tempReg, bail->entry());
+    masm.jump(ool->rejoin());
+
+    return true;
+}
+
 IonScriptCounts *
 CodeGenerator::maybeCreateScriptCounts()
 {
     // If scripts are being profiled, create a new IonScriptCounts for the
     // profiling data, which will be attached to the associated JSScript or
     // AsmJS module after code generation finishes.
     if (!GetIonContext()->runtime->profilingScripts())
         return nullptr;
@@ -3341,16 +3454,19 @@ CodeGenerator::generateBody()
                     return false;
             }
 
 #ifdef CHECK_OSIPOINT_REGISTERS
             if (iter->safepoint())
                 resetOsiPointRegs(iter->safepoint());
 #endif
 
+            if (!callTraceLIR(i, *iter))
+                return false;
+
             if (!iter->accept(this))
                 return false;
 
 #ifdef DEBUG
             if (!counts && !emitDebugResultChecks(*iter))
                 return false;
 #endif
         }
@@ -3789,16 +3905,22 @@ CodeGenerator::visitNewDenseArrayPar(LNe
     saveLive(lir);
     pushArg(lengthReg);
     pushArg(tempReg2);
     if (!callVM(ExtendArrayParInfo, lir))
         return false;
     storeResultTo(ToRegister(lir->output()));
     restoreLive(lir);
 
+    Register resultReg = ToRegister(lir->output());
+    OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, lir);
+    if (!bail)
+        return false;
+    masm.branchTestPtr(Assembler::Zero, resultReg, resultReg, bail->entry());
+
     return true;
 }
 
 typedef JSObject *(*NewStringObjectFn)(JSContext *, HandleString);
 static const VMFunction NewStringObjectInfo = FunctionInfo<NewStringObjectFn>(NewStringObject);
 
 bool
 CodeGenerator::visitNewStringObject(LNewStringObject *lir)
@@ -3896,20 +4018,35 @@ CodeGenerator::visitOutOfLineNewGCThingP
     masm.setupUnalignedABICall(2, out);
     masm.passABIArg(ool->cxReg);
     masm.move32(Imm32(ool->allocKind), out);
     masm.passABIArg(out);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewGCThingPar));
     masm.storeCallResult(out);
     restoreVolatile(out);
 
-    return bailoutTestPtr(Assembler::Zero, out, out, ool->lir->snapshot());
+    OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, ool->lir);
+    if (!bail)
+        return false;
+    masm.branchTestPtr(Assembler::Zero, out, out, bail->entry());
+    masm.jump(ool->rejoin());
+    return true;
 }
 #endif // JSGC_FJGENERATIONAL
 
+bool
+CodeGenerator::visitAbortPar(LAbortPar *lir)
+{
+    OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutUnsupported, lir);
+    if (!bail)
+        return false;
+    masm.jump(bail->entry());
+    return true;
+}
+
 typedef bool(*InitElemFn)(JSContext *cx, HandleObject obj,
                           HandleValue id, HandleValue value);
 static const VMFunction InitElemInfo =
     FunctionInfo<InitElemFn>(InitElemOperation);
 
 bool
 CodeGenerator::visitInitElem(LInitElem *lir)
 {
@@ -8411,16 +8548,39 @@ CodeGenerator::visitProfilerStackOp(LPro
             return true;
 
         default:
             MOZ_ASSUME_UNREACHABLE("invalid LProfilerStackOp type");
     }
 }
 
 bool
+CodeGenerator::visitOutOfLineAbortPar(OutOfLineAbortPar *ool)
+{
+    ParallelBailoutCause cause = ool->cause();
+    jsbytecode *bytecode = ool->bytecode();
+
+    masm.move32(Imm32(cause), CallTempReg0);
+    loadOutermostJSScript(CallTempReg1);
+    loadJSScriptForBlock(ool->basicBlock(), CallTempReg2);
+    masm.movePtr(ImmPtr(bytecode), CallTempReg3);
+
+    masm.setupUnalignedABICall(4, CallTempReg4);
+    masm.passABIArg(CallTempReg0);
+    masm.passABIArg(CallTempReg1);
+    masm.passABIArg(CallTempReg2);
+    masm.passABIArg(CallTempReg3);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, AbortPar));
+
+    masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
+    masm.jump(&returnLabel_);
+    return true;
+}
+
+bool
 CodeGenerator::visitIsCallable(LIsCallable *ins)
 {
     Register object = ToRegister(ins->object());
     Register output = ToRegister(ins->output());
 
     masm.loadObjClass(object, output);
 
     // An object is callable iff (is<JSFunction>() || getClass()->call).
@@ -8454,16 +8614,32 @@ CodeGenerator::loadJSScriptForBlock(MBas
     // The current JSScript means the script for the current
     // basic block. This may be an inlined script.
 
     JSScript *script = block->info().script();
     masm.movePtr(ImmGCPtr(script), reg);
 }
 
 bool
+CodeGenerator::visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool)
+{
+    loadOutermostJSScript(CallTempReg0);
+    loadJSScriptForBlock(ool->lir()->mirRaw()->block(), CallTempReg1);
+
+    masm.setupUnalignedABICall(2, CallTempReg2);
+    masm.passABIArg(CallTempReg0);
+    masm.passABIArg(CallTempReg1);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PropagateAbortPar));
+
+    masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
+    masm.jump(&returnLabel_);
+    return true;
+}
+
+bool
 CodeGenerator::visitHaveSameClass(LHaveSameClass *ins)
 {
     Register lhs = ToRegister(ins->lhs());
     Register rhs = ToRegister(ins->rhs());
     Register temp = ToRegister(ins->getTemp(0));
     Register output = ToRegister(ins->output());
 
     masm.loadObjClass(lhs, temp);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -147,16 +147,17 @@ class CodeGenerator : public CodeGenerat
     bool visitNewDeclEnvObject(LNewDeclEnvObject *lir);
     bool visitNewCallObject(LNewCallObject *lir);
     bool visitNewSingletonCallObject(LNewSingletonCallObject *lir);
     bool visitNewCallObjectPar(LNewCallObjectPar *lir);
     bool visitNewStringObject(LNewStringObject *lir);
     bool visitNewPar(LNewPar *lir);
     bool visitNewDenseArrayPar(LNewDenseArrayPar *lir);
     bool visitNewDerivedTypedObject(LNewDerivedTypedObject *lir);
+    bool visitAbortPar(LAbortPar *lir);
     bool visitInitElem(LInitElem *lir);
     bool visitInitElemGetterSetter(LInitElemGetterSetter *lir);
     bool visitMutateProto(LMutateProto *lir);
     bool visitInitProp(LInitProp *lir);
     bool visitInitPropGetterSetter(LInitPropGetterSetter *lir);
     bool visitCreateThis(LCreateThis *lir);
     bool visitCreateThisWithProto(LCreateThisWithProto *lir);
     bool visitCreateThisWithTemplate(LCreateThisWithTemplate *lir);
@@ -295,28 +296,31 @@ class CodeGenerator : public CodeGenerat
     bool visitAsmJSParameter(LAsmJSParameter *lir);
     bool visitAsmJSReturn(LAsmJSReturn *ret);
     bool visitAsmJSVoidReturn(LAsmJSVoidReturn *ret);
 
     bool visitCheckOverRecursed(LCheckOverRecursed *lir);
     bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
 
     bool visitCheckOverRecursedPar(LCheckOverRecursedPar *lir);
+    bool visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool);
 
     bool visitInterruptCheckPar(LInterruptCheckPar *lir);
     bool visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool);
 
     bool visitInterruptCheckImplicit(LInterruptCheckImplicit *ins);
     bool visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ins);
 
     bool visitUnboxFloatingPoint(LUnboxFloatingPoint *lir);
     bool visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint *ool);
     bool visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool);
 
     bool visitOutOfLineNewGCThingPar(OutOfLineNewGCThingPar *ool);
+    bool visitOutOfLineAbortPar(OutOfLineAbortPar *ool);
+    bool visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool);
     void loadJSScriptForBlock(MBasicBlock *block, Register reg);
     void loadOutermostJSScript(Register reg);
 
     // Inline caches visitors.
     bool visitOutOfLineCache(OutOfLineUpdateCache *ool);
 
     bool visitGetPropertyCacheV(LGetPropertyCacheV *ins);
     bool visitGetPropertyCacheT(LGetPropertyCacheT *ins);
@@ -365,16 +369,17 @@ class CodeGenerator : public CodeGenerat
                             TypedOrValueRegister output, bool monitoredResult,
                             bool allowDoubleResult, jsbytecode *profilerLeavePc);
     bool addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                              PropertyName *name, ConstantOrRegister value, bool strict,
                              bool needsTypeBarrier, jsbytecode *profilerLeavePc);
     bool addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp,
                             FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
                             bool strict, bool guardHoles, jsbytecode *profilerLeavePc);
+    bool checkForAbortPar(LInstruction *lir);
 
     bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
 
     bool emitAllocateGCThingPar(LInstruction *lir, Register objReg, Register cxReg,
                                 Register tempReg1, Register tempReg2,
                                 JSObject *templateObj);
 
     bool emitCallToUncompiledScriptPar(LInstruction *lir, Register calleeReg);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -221,27 +221,20 @@ JitRuntime::initialize(JSContext *cx)
                 break;
             bailoutTables_.infallibleAppend((JitCode *)nullptr);
             bailoutTables_[id] = generateBailoutTable(cx, id);
             if (!bailoutTables_[id])
                 return false;
         }
 
         IonSpew(IonSpew_Codegen, "# Emitting bailout handler");
-        bailoutHandler_ = generateBailoutHandler(cx, SequentialExecution);
+        bailoutHandler_ = generateBailoutHandler(cx);
         if (!bailoutHandler_)
             return false;
 
-#ifdef JS_THREADSAFE
-        IonSpew(IonSpew_Codegen, "# Emitting parallel bailout handler");
-        parallelBailoutHandler_ = generateBailoutHandler(cx, ParallelExecution);
-        if (!parallelBailoutHandler_)
-            return false;
-#endif
-
         IonSpew(IonSpew_Codegen, "# Emitting invalidator");
         invalidator_ = generateInvalidator(cx);
         if (!invalidator_)
             return false;
     }
 
     IonSpew(IonSpew_Codegen, "# Emitting sequential arguments rectifier");
     argumentsRectifier_ = generateArgumentsRectifier(cx, SequentialExecution, &argumentsRectifierReturnAddr_);
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -750,28 +750,38 @@ HandleException(ResumeFromException *rfe
     }
 
     rfe->stackPointer = iter.fp();
 }
 
 void
 HandleParallelFailure(ResumeFromException *rfe)
 {
+    ForkJoinContext *cx = ForkJoinContext::current();
+    JitFrameIterator iter(cx->perThreadData->jitTop, ParallelExecution);
+
     parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry");
 
-    ForkJoinContext *cx = ForkJoinContext::current();
-    JitFrameIterator frameIter(cx);
+    while (!iter.isEntry()) {
+        if (iter.isScripted()) {
+            cx->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM,
+                                           iter.script(), iter.script(), nullptr);
+            break;
+        }
+        ++iter;
+    }
 
-    cx->bailoutRecord->joinCause(ParallelBailoutUnsupportedVM);
-    cx->bailoutRecord->rematerializeFrames(cx, frameIter);
+    while (!iter.isEntry()) {
+        if (iter.isScripted())
+            PropagateAbortPar(iter.script(), iter.script());
+        ++iter;
+    }
 
     rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
-
-    MOZ_ASSERT(frameIter.done());
-    rfe->stackPointer = frameIter.fp();
+    rfe->stackPointer = iter.fp();
 }
 
 void
 EnsureExitFrame(IonCommonFrameLayout *frame)
 {
     if (frame->prevType() == JitFrame_Unwound_IonJS ||
         frame->prevType() == JitFrame_Unwound_BaselineStub ||
         frame->prevType() == JitFrame_Unwound_Rectifier)
--- a/js/src/jit/IonSpewer.cpp
+++ b/js/src/jit/IonSpewer.cpp
@@ -244,16 +244,17 @@ jit::CheckLogging()
             "  bailouts   Bailouts\n"
             "  caches     Inline caches\n"
             "  osi        Invalidation\n"
             "  safepoints Safepoints\n"
             "  pools      Literal Pools (ARM only for now)\n"
             "  cacheflush Instruction Cache flushes (ARM only for now)\n"
             "  range      Range Analysis\n"
             "  logs       C1 and JSON visualization logging\n"
+            "  trace      Generate calls to js::jit::Trace() for effectful instructions\n"
             "  all        Everything\n"
             "\n"
             "  bl-aborts  Baseline compiler abort messages\n"
             "  bl-scripts Baseline script-compilation\n"
             "  bl-op      Baseline compiler detailed op-specific messages\n"
             "  bl-ic      Baseline inline-cache messages\n"
             "  bl-ic-fb   Baseline IC fallback stub messages\n"
             "  bl-osr     Baseline IC OSR messages\n"
@@ -296,16 +297,18 @@ jit::CheckLogging()
     if (ContainsFlag(env, "safepoints"))
         EnableChannel(IonSpew_Safepoints);
     if (ContainsFlag(env, "pools"))
         EnableChannel(IonSpew_Pools);
     if (ContainsFlag(env, "cacheflush"))
         EnableChannel(IonSpew_CacheFlush);
     if (ContainsFlag(env, "logs"))
         EnableIonDebugLogging();
+    if (ContainsFlag(env, "trace"))
+        EnableChannel(IonSpew_Trace);
     if (ContainsFlag(env, "all"))
         LoggingBits = uint32_t(-1);
 
     if (ContainsFlag(env, "bl-aborts"))
         EnableChannel(IonSpew_BaselineAbort);
     if (ContainsFlag(env, "bl-scripts"))
         EnableChannel(IonSpew_BaselineScripts);
     if (ContainsFlag(env, "bl-op"))
--- a/js/src/jit/IonSpewer.h
+++ b/js/src/jit/IonSpewer.h
@@ -49,16 +49,18 @@ namespace jit {
     /* Debug info about snapshots */        \
     _(Snapshots)                            \
     /* Generated inline cache stubs */      \
     _(InlineCaches)                         \
     /* Debug info about safepoints */       \
     _(Safepoints)                           \
     /* Debug info about Pools*/             \
     _(Pools)                                \
+    /* Calls to js::jit::Trace() */         \
+    _(Trace)                                \
     /* Debug info about the I$ */           \
     _(CacheFlush)                           \
                                             \
     /* BASELINE COMPILER SPEW */            \
                                             \
     /* Aborting Script Compilation. */      \
     _(BaselineAbort)                        \
     /* Script Compilation. */               \
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -166,19 +166,16 @@ class JitRuntime
     JitCode *enterBaselineJIT_;
 
     // Vector mapping frame class sizes to bailout tables.
     Vector<JitCode*, 4, SystemAllocPolicy> bailoutTables_;
 
     // Generic bailout table; used if the bailout table overflows.
     JitCode *bailoutHandler_;
 
-    // Bailout handler for parallel execution.
-    JitCode *parallelBailoutHandler_;
-
     // Argument-rectifying thunk, in the case of insufficient arguments passed
     // to a function call site.
     JitCode *argumentsRectifier_;
     void *argumentsRectifierReturnAddr_;
 
     // Arguments-rectifying thunk which loads |parallelIon| instead of |ion|.
     JitCode *parallelArgumentsRectifier_;
 
@@ -235,17 +232,17 @@ class JitRuntime
     js::Value ionReturnOverride_;
 
   private:
     JitCode *generateExceptionTailStub(JSContext *cx);
     JitCode *generateBailoutTailStub(JSContext *cx);
     JitCode *generateEnterJIT(JSContext *cx, EnterJitType type);
     JitCode *generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut);
     JitCode *generateBailoutTable(JSContext *cx, uint32_t frameClass);
-    JitCode *generateBailoutHandler(JSContext *cx, ExecutionMode mode);
+    JitCode *generateBailoutHandler(JSContext *cx);
     JitCode *generateInvalidator(JSContext *cx);
     JitCode *generatePreBarrier(JSContext *cx, MIRType type);
     JitCode *generateMallocStub(JSContext *cx);
     JitCode *generateFreeStub(JSContext *cx);
     JitCode *generateDebugTrapHandler(JSContext *cx);
     JitCode *generateForkJoinGetSliceStub(JSContext *cx);
     JitCode *generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrameRegPopOffsetOut);
     JitCode *generateVMWrapper(JSContext *cx, const VMFunction &f);
@@ -302,22 +299,18 @@ class JitRuntime
 
     bool handleAccessViolation(JSRuntime *rt, void *faultingAddress);
 
     JitCode *getVMWrapper(const VMFunction &f) const;
     JitCode *debugTrapHandler(JSContext *cx);
     JitCode *getBaselineDebugModeOSRHandler(JSContext *cx);
     void *getBaselineDebugModeOSRHandlerAddress(JSContext *cx, bool popFrameReg);
 
-    JitCode *getGenericBailoutHandler(ExecutionMode mode) const {
-        switch (mode) {
-          case SequentialExecution: return bailoutHandler_;
-          case ParallelExecution:   return parallelBailoutHandler_;
-          default:                  MOZ_ASSUME_UNREACHABLE("No such execution mode");
-        }
+    JitCode *getGenericBailoutHandler() const {
+        return bailoutHandler_;
     }
 
     JitCode *getExceptionTail() const {
         return exceptionTail_;
     }
 
     JitCode *getBailoutTail() const {
         return bailoutTail_;
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -566,16 +566,22 @@ class LNewStringObject : public LInstruc
     const LDefinition *temp() {
         return getTemp(0);
     }
     MNewStringObject *mir() const {
         return mir_->toNewStringObject();
     }
 };
 
+class LAbortPar : public LInstructionHelper<0, 0, 0>
+{
+  public:
+    LIR_HEADER(AbortPar);
+};
+
 class LInitElem : public LCallInstructionHelper<0, 1 + 2*BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(InitElem)
 
     explicit LInitElem(const LAllocation &object) {
         setOperand(0, object);
     }
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -29,16 +29,17 @@
     _(NewDeclEnvObject)             \
     _(NewCallObject)                \
     _(NewSingletonCallObject)       \
     _(NewStringObject)              \
     _(NewPar)                       \
     _(NewDenseArrayPar)             \
     _(NewCallObjectPar)             \
     _(NewDerivedTypedObject)        \
+    _(AbortPar)                     \
     _(InitElem)                     \
     _(InitElemGetterSetter)         \
     _(MutateProto)                  \
     _(InitProp)                     \
     _(InitPropGetterSetter)         \
     _(CheckOverRecursed)            \
     _(CheckOverRecursedPar)         \
     _(DefVar)                       \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -113,17 +113,23 @@ LIRGenerator::visitTableSwitch(MTableSwi
     }
     return add(newLTableSwitch(index, tempInt, tableswitch));
 }
 
 bool
 LIRGenerator::visitCheckOverRecursed(MCheckOverRecursed *ins)
 {
     LCheckOverRecursed *lir = new(alloc()) LCheckOverRecursed();
-    return add(lir, ins) && assignSafepoint(lir, ins);
+
+    if (!add(lir, ins))
+        return false;
+    if (!assignSafepoint(lir, ins))
+        return false;
+
+    return true;
 }
 
 bool
 LIRGenerator::visitCheckOverRecursedPar(MCheckOverRecursedPar *ins)
 {
     LCheckOverRecursedPar *lir =
         new(alloc()) LCheckOverRecursedPar(useRegister(ins->forkJoinContext()), temp());
     return add(lir, ins) && assignSafepoint(lir, ins);
@@ -227,16 +233,23 @@ LIRGenerator::visitNewStringObject(MNewS
 {
     JS_ASSERT(ins->input()->type() == MIRType_String);
 
     LNewStringObject *lir = new(alloc()) LNewStringObject(useRegister(ins->input()), temp());
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitAbortPar(MAbortPar *ins)
+{
+    LAbortPar *lir = new(alloc()) LAbortPar();
+    return add(lir, ins);
+}
+
+bool
 LIRGenerator::visitInitElem(MInitElem *ins)
 {
     LInitElem *lir = new(alloc()) LInitElem(useRegisterAtStart(ins->getObject()));
     if (!useBoxAtStart(lir, LInitElem::IdIndex, ins->getId()))
         return false;
     if (!useBoxAtStart(lir, LInitElem::ValueIndex, ins->getValue()))
         return false;
 
@@ -2161,17 +2174,17 @@ LIRGenerator::visitGuardThreadExclusive(
     // general form of write guard check. we could employ TI feedback
     // to optimize this if we know that the object being tested is a
     // typed object or know that it is definitely NOT a typed object.
     LGuardThreadExclusive *lir =
         new(alloc()) LGuardThreadExclusive(useFixed(ins->forkJoinContext(), CallTempReg0),
                                            useFixed(ins->object(), CallTempReg1),
                                            tempFixed(CallTempReg2));
     lir->setMir(ins);
-    return assignSnapshot(lir, Bailout_GuardThreadExclusive) && add(lir, ins);
+    return add(lir, ins);
 }
 
 bool
 LIRGenerator::visitInterruptCheck(MInterruptCheck *ins)
 {
     // Implicit interrupt checks require asm.js signal handlers to be
     // installed. ARM does not yet use implicit interrupt checks, see
     // bug 864220.
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -72,16 +72,17 @@ class LIRGenerator : public LIRGenerator
     bool visitNewDeclEnvObject(MNewDeclEnvObject *ins);
     bool visitNewCallObject(MNewCallObject *ins);
     bool visitNewRunOnceCallObject(MNewRunOnceCallObject *ins);
     bool visitNewStringObject(MNewStringObject *ins);
     bool visitNewDerivedTypedObject(MNewDerivedTypedObject *ins);
     bool visitNewPar(MNewPar *ins);
     bool visitNewCallObjectPar(MNewCallObjectPar *ins);
     bool visitNewDenseArrayPar(MNewDenseArrayPar *ins);
+    bool visitAbortPar(MAbortPar *ins);
     bool visitInitElem(MInitElem *ins);
     bool visitInitElemGetterSetter(MInitElemGetterSetter *ins);
     bool visitMutateProto(MMutateProto *ins);
     bool visitInitProp(MInitProp *ins);
     bool visitInitPropGetterSetter(MInitPropGetterSetter *ins);
     bool visitCheckOverRecursed(MCheckOverRecursed *ins);
     bool visitCheckOverRecursedPar(MCheckOverRecursedPar *ins);
     bool visitDefVar(MDefVar *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1816,16 +1816,34 @@ class MNewDerivedTypedObject
     }
 
     bool writeRecoverData(CompactBufferWriter &writer) const;
     bool canRecoverOnBailout() const {
         return true;
     }
 };
 
+// Abort parallel execution.
+class MAbortPar : public MAryControlInstruction<0, 0>
+{
+    MAbortPar()
+      : MAryControlInstruction<0, 0>()
+    {
+        setResultType(MIRType_None);
+        setGuard();
+    }
+
+  public:
+    INSTRUCTION_HEADER(AbortPar);
+
+    static MAbortPar *New(TempAllocator &alloc) {
+        return new(alloc) MAbortPar();
+    }
+};
+
 // Setting __proto__ in an object literal.
 class MMutateProto
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
 {
   protected:
     MMutateProto(MDefinition *obj, MDefinition *value)
     {
@@ -2269,21 +2287,20 @@ class MApplyArgs
     TypePolicy *typePolicy() {
         return this;
     }
     bool possiblyCalls() const {
         return true;
     }
 };
 
-class MBail : public MAryControlInstruction<0, 0>
+class MBail : public MNullaryInstruction
 {
   protected:
     MBail(BailoutKind kind)
-      : MAryControlInstruction<0, 0>()
     {
         bailoutKind_ = kind;
         setGuard();
     }
 
   private:
     BailoutKind bailoutKind_;
 
@@ -5231,31 +5248,29 @@ class MCheckOverRecursedPar : public MUn
 // Check for an interrupt (or rendezvous) in parallel mode.
 class MInterruptCheckPar : public MUnaryInstruction
 {
     explicit MInterruptCheckPar(MDefinition *cx)
       : MUnaryInstruction(cx)
     {
         setResultType(MIRType_None);
         setGuard();
+        setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(InterruptCheckPar);
 
     static MInterruptCheckPar *New(TempAllocator &alloc, MDefinition *cx) {
         return new(alloc) MInterruptCheckPar(cx);
     }
 
     MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
-    AliasSet getAliasSet() const {
-        return AliasSet::None();
-    }
 };
 
 // Check whether we need to fire the interrupt handler.
 class MInterruptCheck : public MNullaryInstruction
 {
     MInterruptCheck() {
         setGuard();
     }
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -743,25 +743,17 @@ MBasicBlock::discardDefAt(MDefinitionIte
         iter.iter_ = iter.block_->discardAt(iter.iter_);
 
     return iter;
 }
 
 void
 MBasicBlock::discardAllInstructions()
 {
-    MInstructionIterator iter = begin();
-    discardAllInstructionsStartingAt(iter);
-
-}
-
-void
-MBasicBlock::discardAllInstructionsStartingAt(MInstructionIterator &iter)
-{
-    while (iter != end()) {
+    for (MInstructionIterator iter = begin(); iter != end(); ) {
         for (size_t i = 0, e = iter->numOperands(); i < e; i++)
             iter->discardOperand(i);
         iter = instructions_.removeAt(iter);
     }
 }
 
 void
 MBasicBlock::discardAllPhiOperands()
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -227,17 +227,16 @@ class MBasicBlock : public TempObject, p
 
     // Removes an instruction with the intention to discard it.
     void discard(MInstruction *ins);
     void discardLastIns();
     MInstructionIterator discardAt(MInstructionIterator &iter);
     MInstructionReverseIterator discardAt(MInstructionReverseIterator &iter);
     MDefinitionIterator discardDefAt(MDefinitionIterator &iter);
     void discardAllInstructions();
-    void discardAllInstructionsStartingAt(MInstructionIterator &iter);
     void discardAllPhiOperands();
     void discardAllPhis();
     void discardAllResumePoints(bool discardEntry = true);
 
     // Discards a phi instruction and updates predecessor successorWithPhis.
     MPhiIterator discardPhiAt(MPhiIterator &at);
 
     // Mark this block as having been removed from the graph.
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -212,16 +212,17 @@ namespace jit {
     _(AsmJSVoidReturn)                                                      \
     _(AsmJSPassStackArg)                                                    \
     _(AsmJSCall)                                                            \
     _(CheckOverRecursedPar)                                                 \
     _(NewCallObjectPar)                                                     \
     _(NewPar)                                                               \
     _(NewDenseArrayPar)                                                     \
     _(NewDerivedTypedObject)                                                \
+    _(AbortPar)                                                             \
     _(LambdaPar)                                                            \
     _(RestPar)                                                              \
     _(ForkJoinContext)                                                      \
     _(ForkJoinGetSlice)                                                     \
     _(GuardThreadExclusive)                                                 \
     _(InterruptCheckPar)                                                    \
     _(RecompileCheck)
 
--- a/js/src/jit/ParallelFunctions.cpp
+++ b/js/src/jit/ParallelFunctions.cpp
@@ -4,30 +4,30 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/ParallelFunctions.h"
 
 #include "builtin/TypedObject.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/mips/Simulator-mips.h"
-#include "jit/RematerializedFrame.h"
 #include "vm/ArrayObject.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace jit;
 
 using JS::AutoCheckCannotGC;
 
 using parallel::Spew;
 using parallel::SpewOps;
 using parallel::SpewBailouts;
+using parallel::SpewBailoutIR;
 
 // Load the current thread context.
 ForkJoinContext *
 jit::ForkJoinContextPar()
 {
     return ForkJoinContext::current();
 }
 
@@ -120,16 +120,73 @@ bool
 jit::IsInTargetRegion(ForkJoinContext *cx, TypedObject *typedObj)
 {
     JS_ASSERT(typedObj->is<TypedObject>()); // in case JIT supplies something bogus
     uint8_t *typedMem = typedObj->typedMem();
     return (typedMem >= cx->targetRegionStart &&
             typedMem <  cx->targetRegionEnd);
 }
 
+#ifdef DEBUG
+static void
+printTrace(const char *prefix, struct IonLIRTraceData *cached)
+{
+    fprintf(stderr, "%s / Block %3u / LIR %3u / Mode %u / LIR %s\n",
+            prefix,
+            cached->blockIndex, cached->lirIndex, cached->execModeInt, cached->lirOpName);
+}
+
+static struct IonLIRTraceData seqTraceData;
+#endif
+
+void
+jit::TraceLIR(IonLIRTraceData *current)
+{
+#ifdef DEBUG
+    static enum { NotSet, All, Bailouts } traceMode;
+
+    // If you set IONFLAGS=trace, this function will be invoked before every LIR.
+    //
+    // You can either modify it to do whatever you like, or use gdb scripting.
+    // For example:
+    //
+    // break TraceLIR
+    // commands
+    // continue
+    // exit
+
+    if (traceMode == NotSet) {
+        // Racy, but that's ok.
+        const char *env = getenv("IONFLAGS");
+        if (strstr(env, "trace-all"))
+            traceMode = All;
+        else
+            traceMode = Bailouts;
+    }
+
+    IonLIRTraceData *cached;
+    if (current->execModeInt == 0)
+        cached = &seqTraceData;
+    else
+        cached = &ForkJoinContext::current()->traceData;
+
+    if (current->blockIndex == 0xDEADBEEF) {
+        if (current->execModeInt == 0)
+            printTrace("BAILOUT", cached);
+        else
+            SpewBailoutIR(cached);
+    }
+
+    memcpy(cached, current, sizeof(IonLIRTraceData));
+
+    if (traceMode == All)
+        printTrace("Exec", cached);
+#endif
+}
+
 bool
 jit::CheckOverRecursedPar(ForkJoinContext *cx)
 {
     JS_ASSERT(ForkJoinContext::current() == cx);
     int stackDummy_;
 
     // When an interrupt is requested, the main thread stack limit is
     // overwritten with a sentinel value that brings us here.
@@ -137,56 +194,58 @@ jit::CheckOverRecursedPar(ForkJoinContex
     // and, if not, check whether an interrupt was requested.
     //
     // When not on the main thread, we don't overwrite the stack
     // limit, but we do still call into this routine if the interrupt
     // flag is set, so we still need to double check.
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     if (Simulator::Current()->overRecursed()) {
-        cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
+        cx->bailoutRecord->setCause(ParallelBailoutOverRecursed);
         return false;
     }
 #endif
 
     uintptr_t realStackLimit;
     if (cx->isMainThread())
         realStackLimit = GetNativeStackLimit(cx);
     else
         realStackLimit = cx->perThreadData->jitStackLimit;
 
     if (!JS_CHECK_STACK_SIZE(realStackLimit, &stackDummy_)) {
-        cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
+        cx->bailoutRecord->setCause(ParallelBailoutOverRecursed);
         return false;
     }
 
     return InterruptCheckPar(cx);
 }
 
 bool
 jit::InterruptCheckPar(ForkJoinContext *cx)
 {
     JS_ASSERT(ForkJoinContext::current() == cx);
     bool result = cx->check();
     if (!result) {
-        cx->bailoutRecord->joinCause(ParallelBailoutInterrupt);
+        // Do not set the cause here.  Either it was set by this
+        // thread already by some code that then triggered an abort,
+        // or else we are just picking up an abort from some other
+        // thread.  Either way we have nothing useful to contribute so
+        // we might as well leave our bailout case unset.
         return false;
     }
     return true;
 }
 
 JSObject *
 jit::ExtendArrayPar(ForkJoinContext *cx, JSObject *array, uint32_t length)
 {
     JSObject::EnsureDenseResult res =
         array->ensureDenseElementsPreservePackedFlag(cx, 0, length);
-    if (res != JSObject::ED_OK) {
-        fprintf(stderr, "==== NGNG\n");
+    if (res != JSObject::ED_OK)
         return nullptr;
-    }
     return array;
 }
 
 bool
 jit::SetPropertyPar(ForkJoinContext *cx, HandleObject obj, HandlePropertyName name,
                     HandleValue value, bool strict, jsbytecode *pc)
 {
     JS_ASSERT(cx->isThreadLocal(obj));
@@ -509,46 +568,68 @@ jit::UrshValuesPar(ForkJoinContext *cx, 
     if (!NonObjectToUint32(cx, lhs, &left) || !NonObjectToInt32(cx, rhs, &right))
         return false;
     left >>= right & 31;
     out.setNumber(uint32_t(left));
     return true;
 }
 
 void
-jit::BailoutPar(BailoutStack *sp, uint8_t **entryFramePointer)
+jit::AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
+              jsbytecode *bytecode)
 {
-    parallel::Spew(parallel::SpewBailouts, "Bailing");
+    // Spew before asserts to help with diagnosing failures.
+    Spew(SpewBailouts,
+         "Parallel abort with cause %d in %p:%s:%d "
+         "(%p:%s:%d at line %d)",
+         cause,
+         outermostScript, outermostScript->filename(), outermostScript->lineno(),
+         currentScript, currentScript->filename(), currentScript->lineno(),
+         (currentScript ? PCToLineNumber(currentScript, bytecode) : 0));
+
+    JS_ASSERT(InParallelSection());
+    JS_ASSERT(outermostScript != nullptr);
+    JS_ASSERT(currentScript != nullptr);
+    JS_ASSERT(outermostScript->hasParallelIonScript());
 
     ForkJoinContext *cx = ForkJoinContext::current();
 
-    // We don't have an exit frame.
-    MOZ_ASSERT(size_t(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(IonCommonFrameLayout)) < 0x1000 &&
-               size_t(FAKE_JIT_TOP_FOR_BAILOUT) >= 0,
-               "Fake jitTop pointer should be within the first page.");
-    cx->perThreadData->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
-
-    JitActivationIterator jitActivations(cx->perThreadData);
-    IonBailoutIterator frameIter(jitActivations, sp);
-    cx->bailoutRecord->joinCause(ParallelBailoutUnsupported);
-    cx->bailoutRecord->rematerializeFrames(cx, frameIter);
-
-    MOZ_ASSERT(frameIter.done());
-    *entryFramePointer = frameIter.fp();
+    JS_ASSERT(cx->bailoutRecord->depth == 0);
+    cx->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode);
 }
 
-bool
-jit::CallToUncompiledScriptPar(ForkJoinContext *cx, JSObject *obj)
+void
+jit::PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript)
 {
+    Spew(SpewBailouts,
+         "Propagate parallel abort via %p:%s:%d (%p:%s:%d)",
+         outermostScript, outermostScript->filename(), outermostScript->lineno(),
+         currentScript, currentScript->filename(), currentScript->lineno());
+
+    JS_ASSERT(InParallelSection());
+    JS_ASSERT(outermostScript->hasParallelIonScript());
+
+    outermostScript->parallelIonScript()->setHasUncompiledCallTarget();
+
+    ForkJoinContext *cx = ForkJoinContext::current();
+    if (currentScript)
+        cx->bailoutRecord->addTrace(currentScript, nullptr);
+}
+
+void
+jit::CallToUncompiledScriptPar(JSObject *obj)
+{
+    JS_ASSERT(InParallelSection());
+
 #ifdef DEBUG
     static const int max_bound_function_unrolling = 5;
 
     if (!obj->is<JSFunction>()) {
         Spew(SpewBailouts, "Call to non-function");
-        return false;
+        return;
     }
 
     JSFunction *func = &obj->as<JSFunction>();
     if (func->hasScript()) {
         JSScript *script = func->nonLazyScript();
         Spew(SpewBailouts, "Call to uncompiled script: %p:%s:%d",
              script, script->filename(), script->lineno());
     } else if (func->isInterpretedLazy()) {
@@ -570,18 +651,16 @@ jit::CallToUncompiledScriptPar(ForkJoinC
         } else {
             Spew(SpewBailouts, "Call to bound function (excessive depth: %d)", depth);
         }
     } else {
         JS_ASSERT(func->isNative());
         Spew(SpewBailouts, "Call to native function");
     }
 #endif
-
-    return false;
 }
 
 JSObject *
 jit::InitRestParameterPar(ForkJoinContext *cx, uint32_t length, Value *rest,
                           HandleObject templateObj, HandleObject res)
 {
     // In parallel execution, we should always have succeeded in allocation
     // before this point. We can do the allocation here like in the sequential
--- a/js/src/jit/ParallelFunctions.h
+++ b/js/src/jit/ParallelFunctions.h
@@ -67,15 +67,20 @@ bool BitRshPar(ForkJoinContext *cx, Hand
 
 bool UrshValuesPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out);
 
 // Make a new rest parameter in parallel.
 JSObject *InitRestParameterPar(ForkJoinContext *cx, uint32_t length, Value *rest,
                                HandleObject templateObj, HandleObject res);
 
 // Abort and debug tracing functions.
-void BailoutPar(BailoutStack *sp, uint8_t **entryFramePointer);
-bool CallToUncompiledScriptPar(ForkJoinContext *cx, JSObject *obj);
+void AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
+              jsbytecode *bytecode);
+void PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript);
+
+void TraceLIR(IonLIRTraceData *current);
+
+void CallToUncompiledScriptPar(JSObject *obj);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_ParallelFunctions_h */
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -70,17 +70,16 @@ class ParallelSafetyVisitor : public MIn
     MIRGraph &graph_;
     bool unsafe_;
     MDefinition *cx_;
 
     bool insertWriteGuard(MInstruction *writeInstruction, MDefinition *valueBeingWritten);
 
     bool replaceWithNewPar(MInstruction *newInstruction, JSObject *templateObject);
     bool replace(MInstruction *oldInstruction, MInstruction *replacementInstruction);
-    bool replaceLastIns(MInstruction *oldInstruction, MControlInstruction *replacementInstruction);
 
     bool visitSpecializedInstruction(MInstruction *ins, MIRType spec, uint32_t flags);
 
     // Intended for use in a visitXyz() instruction like "return
     // markUnsafe()".  Sets the unsafe flag and returns true (since
     // this does not indicate an unrecoverable compilation failure).
     bool markUnsafe() {
         JS_ASSERT(!unsafe_);
@@ -102,17 +101,17 @@ class ParallelSafetyVisitor : public MIn
     void clearUnsafe() { unsafe_ = false; }
     bool unsafe() { return unsafe_; }
     MDefinition *ForkJoinContext() {
         if (!cx_)
             cx_ = graph_.forkJoinContext();
         return cx_;
     }
 
-    bool convertToBailout(MInstructionIterator &iter);
+    bool convertToBailout(MBasicBlock *block, MInstruction *ins);
 
     // I am taking the policy of blacklisting everything that's not
     // obviously safe for now.  We can loosen as we need.
 
     SAFE_OP(Constant)
     UNSAFE_OP(CloneLiteral)
     SAFE_OP(Parameter)
     SAFE_OP(Callee)
@@ -277,16 +276,17 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(InstanceOf)
     CUSTOM_OP(InterruptCheck)
     SAFE_OP(ForkJoinContext)
     SAFE_OP(ForkJoinGetSlice)
     SAFE_OP(NewPar)
     SAFE_OP(NewDenseArrayPar)
     SAFE_OP(NewCallObjectPar)
     SAFE_OP(LambdaPar)
+    SAFE_OP(AbortPar)
     UNSAFE_OP(ArrayConcat)
     UNSAFE_OP(GetDOMProperty)
     UNSAFE_OP(GetDOMMember)
     UNSAFE_OP(SetDOMProperty)
     UNSAFE_OP(NewStringObject)
     UNSAFE_OP(Random)
     SAFE_OP(Pow)
     SAFE_OP(PowHalf)
@@ -324,26 +324,16 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(AsmJSParameter)
     UNSAFE_OP(AsmJSCall)
     DROP_OP(RecompileCheck)
 
     // It looks like this could easily be made safe:
     UNSAFE_OP(ConvertElementsToDoubles)
 };
 
-static void
-TransplantResumePoint(MInstruction *oldInstruction, MInstruction *replacementInstruction)
-{
-    if (MResumePoint *rp = oldInstruction->resumePoint()) {
-        replacementInstruction->setResumePoint(rp);
-        if (rp->instruction() == oldInstruction)
-            rp->setInstruction(replacementInstruction);
-    }
-}
-
 bool
 ParallelSafetyAnalysis::analyze()
 {
     // Walk the basic blocks in a DFS.  When we encounter a block with an
     // unsafe instruction, then we know that this block will bailout when
     // executed.  Therefore, we replace the block.
     //
     // We don't need a worklist, though, because the graph is sorted
@@ -359,30 +349,31 @@ ParallelSafetyAnalysis::analyze()
         if (block->isMarked()) {
             // Count the number of reachable blocks.
             marked++;
 
             // Iterate through and transform the instructions.  Stop
             // if we encounter an inherently unsafe operation, in
             // which case we will transform this block into a bailout
             // block.
-            MInstruction *ins = nullptr;
-            MInstructionIterator iter(block->begin());
-            while (iter != block->end() && !visitor.unsafe()) {
+            MInstruction *instr = nullptr;
+            for (MInstructionIterator ins(block->begin());
+                 ins != block->end() && !visitor.unsafe();)
+            {
                 if (mir_->shouldCancel("ParallelSafetyAnalysis"))
                     return false;
 
                 // We may be removing or replacing the current
-                // instruction, so advance `iter` now.  Remember the
+                // instruction, so advance `ins` now.  Remember the
                 // last instr. we looked at for use later if it should
                 // prove unsafe.
-                ins = *iter++;
+                instr = *ins++;
 
-                if (!ins->accept(&visitor)) {
-                    SpewMIR(ins, "Unaccepted");
+                if (!instr->accept(&visitor)) {
+                    SpewMIR(instr, "Unaccepted");
                     return false;
                 }
             }
 
             if (!visitor.unsafe()) {
                 // Block consists of only safe instructions.  Visit its successors.
                 for (uint32_t i = 0; i < block->numSuccessors(); i++)
                     block->getSuccessor(i)->markUnchecked();
@@ -394,59 +385,105 @@ ParallelSafetyAnalysis::analyze()
                 // in even trying to execute this function as it will
                 // always bailout.
                 if (*block == graph_.entryBlock()) {
                     Spew(SpewCompile, "Entry block contains unsafe MIR");
                     mir_->disable();
                     return false;
                 }
 
-                // Otherwise, create a replacement that will. We seek back one
-                // position on the instruction iterator, as we will be
-                // discarding all instructions starting at the unsafe
-                // instruction.
-                if (!visitor.convertToBailout(--iter))
+                // Otherwise, create a replacement that will.
+                if (!visitor.convertToBailout(*block, instr))
                     return false;
             }
         }
     }
 
     Spew(SpewCompile, "Safe");
     IonSpewPass("ParallelSafetyAnalysis");
 
     UnreachableCodeElimination uce(mir_, graph_);
     if (!uce.removeUnmarkedBlocks(marked))
         return false;
     IonSpewPass("UCEAfterParallelSafetyAnalysis");
     AssertExtendedGraphCoherency(graph_);
 
+    if (!removeResumePointOperands())
+        return false;
+    IonSpewPass("RemoveResumePointOperands");
+    AssertExtendedGraphCoherency(graph_);
+
     return true;
 }
 
 bool
-ParallelSafetyVisitor::convertToBailout(MInstructionIterator &iter)
+ParallelSafetyAnalysis::removeResumePointOperands()
 {
-    // We expect iter to be settled on the unsafe instruction.
-    MInstruction *ins = *iter;
-    MBasicBlock *block = ins->block();
+    // In parallel exec mode, nothing is effectful, therefore we do
+    // not need to reconstruct interpreter state and can simply
+    // bailout by returning a special code.  Ideally we'd either
+    // remove the unused resume points or else never generate them in
+    // the first place, but I encountered various assertions and
+    // crashes attempting to do that, so for the time being I simply
+    // replace their operands with undefined.  This prevents them from
+    // interfering with DCE and other optimizations.  It is also *necessary*
+    // to handle cases like this:
+    //
+    //     foo(a, b, c.bar())
+    //
+    // where `foo` was deemed to be an unsafe function to call.  This
+    // is because without neutering the ResumePoints, they would still
+    // refer to the MPassArg nodes generated for the call to foo().
+    // But the call to foo() is dead and has been removed, leading to
+    // an inconsistent IR and assertions at codegen time.
+
+    MConstant *udef = nullptr;
+    for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
+        if (udef)
+            replaceOperandsOnResumePoint(block->entryResumePoint(), udef);
+
+        for (MInstructionIterator ins(block->begin()); ins != block->end(); ins++) {
+            if (ins->isStart()) {
+                JS_ASSERT(udef == nullptr);
+                udef = MConstant::New(graph_.alloc(), UndefinedValue());
+                block->insertAfter(*ins, udef);
+            } else if (udef) {
+                if (MResumePoint *resumePoint = ins->resumePoint())
+                    replaceOperandsOnResumePoint(resumePoint, udef);
+            }
+        }
+    }
+    return true;
+}
+
+void
+ParallelSafetyAnalysis::replaceOperandsOnResumePoint(MResumePoint *resumePoint,
+                                                     MDefinition *withDef)
+{
+    for (size_t i = 0, e = resumePoint->numOperands(); i < e; i++)
+        resumePoint->replaceOperand(i, withDef);
+}
+
+bool
+ParallelSafetyVisitor::convertToBailout(MBasicBlock *block, MInstruction *ins)
+{
     JS_ASSERT(unsafe()); // `block` must have contained unsafe items
     JS_ASSERT(block->isMarked()); // `block` must have been reachable to get here
 
-    clearUnsafe();
-
-    // Discard the rest of the block and sever its link to its successors in
-    // the CFG.
-    for (size_t i = 0; i < block->numSuccessors(); i++)
+    // Convert the block to a bailout block.  In principle, we
+    // only need one bailout block per graph! But I
+    // found this approach easier to implement given the design of the
+    // MIR Graph construction routines. Using multiple blocks helps to
+    // keep the PC information more accurate.
+    for (size_t i = 0, e = block->numSuccessors(); i < e; i++)
         block->getSuccessor(i)->removePredecessor(block);
-    block->discardAllInstructionsStartingAt(iter);
-
-    // End the block in the bail.
-    MBail *bailout = MBail::New(graph_.alloc());
-    TransplantResumePoint(ins, bailout);
-    block->end(bailout);
+    clearUnsafe();
+    block->discardAllPhis();
+    block->discardAllInstructions();
+    block->end(MAbortPar::New(graph_.alloc()));
     return true;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Memory allocation
 //
 // Simple memory allocation opcodes---those which ultimately compile
 // down to a (possibly inlined) invocation of NewGCThing()---are
@@ -461,41 +498,42 @@ ParallelSafetyVisitor::visitCreateThisWi
 
 bool
 ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins)
 {
     if (ins->templateObject()->hasDynamicSlots()) {
         SpewMIR(ins, "call with dynamic slots");
         return markUnsafe();
     }
-
-    return replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
+    replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
+    return true;
 }
 
 bool
 ParallelSafetyVisitor::visitNewRunOnceCallObject(MNewRunOnceCallObject *ins)
 {
     if (ins->templateObject()->hasDynamicSlots()) {
         SpewMIR(ins, "call with dynamic slots");
         return markUnsafe();
     }
-
-    return replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
+    replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
+    return true;
 }
 
 bool
 ParallelSafetyVisitor::visitLambda(MLambda *ins)
 {
     if (ins->info().singletonType || ins->info().useNewTypeForClone) {
         // slow path: bail on parallel execution.
         return markUnsafe();
     }
 
     // fast path: replace with LambdaPar op
-    return replace(ins, MLambdaPar::New(alloc(), ForkJoinContext(), ins));
+    replace(ins, MLambdaPar::New(alloc(), ForkJoinContext(), ins));
+    return true;
 }
 
 bool
 ParallelSafetyVisitor::visitNewObject(MNewObject *newInstruction)
 {
     if (newInstruction->shouldUseVM()) {
         SpewMIR(newInstruction, "should use VM");
         return markUnsafe();
@@ -555,28 +593,32 @@ ParallelSafetyVisitor::visitToString(MTo
         return markUnsafe();
     return true;
 }
 
 bool
 ParallelSafetyVisitor::replaceWithNewPar(MInstruction *newInstruction,
                                          JSObject *templateObject)
 {
-    return replace(newInstruction, MNewPar::New(alloc(), ForkJoinContext(), templateObject));
+    replace(newInstruction, MNewPar::New(alloc(), ForkJoinContext(), templateObject));
+    return true;
 }
 
 bool
 ParallelSafetyVisitor::replace(MInstruction *oldInstruction,
                                MInstruction *replacementInstruction)
 {
-    TransplantResumePoint(oldInstruction, replacementInstruction);
-
     MBasicBlock *block = oldInstruction->block();
     block->insertBefore(oldInstruction, replacementInstruction);
     oldInstruction->replaceAllUsesWith(replacementInstruction);
+    MResumePoint *rp = oldInstruction->resumePoint();
+    if (rp && rp->instruction() == oldInstruction) {
+        rp->setInstruction(replacementInstruction);
+        replacementInstruction->setResumePoint(rp);
+    }
     block->discard(oldInstruction);
 
     // We may have replaced a specialized Float32 instruction by its
     // non-specialized version, so just retry to specialize it. This relies on
     // the fact that Phis' types don't change during the ParallelSafetyAnalysis;
     // otherwise we'd have to run the entire TypeAnalyzer Float32 analysis once
     // instructions have been replaced.
     if (replacementInstruction->isFloat32Commutative() &&
@@ -584,27 +626,16 @@ ParallelSafetyVisitor::replace(MInstruct
     {
         replacementInstruction->trySpecializeFloat32(alloc());
     }
     JS_ASSERT(oldInstruction->type() == replacementInstruction->type());
 
     return true;
 }
 
-bool
-ParallelSafetyVisitor::replaceLastIns(MInstruction *oldInstruction,
-                                      MControlInstruction *replacementInstruction)
-{
-    TransplantResumePoint(oldInstruction, replacementInstruction);
-    MBasicBlock *block = oldInstruction->block();
-    block->discardLastIns();
-    block->end(replacementInstruction);
-    return true;
-}
-
 /////////////////////////////////////////////////////////////////////////////
 // Write Guards
 //
 // We only want to permit writes to locally guarded objects.
 // Furthermore, we want to avoid PICs and other non-thread-safe things
 // (though perhaps we should support PICs at some point).  If we
 // cannot determine the origin of an object, we can insert a write
 // guard which will check whether the object was allocated from the
@@ -758,18 +789,23 @@ ParallelSafetyVisitor::visitSpecializedI
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Throw
 
 bool
 ParallelSafetyVisitor::visitThrow(MThrow *thr)
 {
-    JS_ASSERT(thr->block()->lastIns() == thr);
-    replaceLastIns(thr, MBail::New(alloc()));
+    MBasicBlock *block = thr->block();
+    JS_ASSERT(block->lastIns() == thr);
+    block->discardLastIns();
+    MAbortPar *bailout = MAbortPar::New(alloc());
+    if (!bailout)
+        return false;
+    block->end(bailout);
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Callee extraction
 //
 // See comments in header file.
 
--- a/js/src/jit/ParallelSafetyAnalysis.h
+++ b/js/src/jit/ParallelSafetyAnalysis.h
@@ -15,22 +15,25 @@ class InterpreterFrame;
 
 namespace jit {
 
 class MIRGraph;
 class AutoDestroyAllocator;
 
 // Determines whether a function is compatible for parallel execution.
 // Removes basic blocks containing unsafe MIR operations from the
-// graph and replaces them with MBail blocks.
+// graph and replaces them with MAbortPar blocks.
 class ParallelSafetyAnalysis
 {
     MIRGenerator *mir_;
     MIRGraph &graph_;
 
+    bool removeResumePointOperands();
+    void replaceOperandsOnResumePoint(MResumePoint *resumePoint, MDefinition *withDef);
+
   public:
     ParallelSafetyAnalysis(MIRGenerator *mir,
                            MIRGraph &graph)
       : mir_(mir),
         graph_(graph)
     {}
 
     bool analyze();
--- a/js/src/jit/RematerializedFrame.cpp
+++ b/js/src/jit/RematerializedFrame.cpp
@@ -24,83 +24,42 @@ struct CopyValueToRematerializedFrame
     { }
 
     void operator()(const Value &v) {
         *slots++ = v;
     }
 };
 
 RematerializedFrame::RematerializedFrame(ThreadSafeContext *cx, uint8_t *top,
-                                         unsigned numActualArgs, InlineFrameIterator &iter)
+                                         InlineFrameIterator &iter)
   : prevUpToDate_(false),
     top_(top),
-    pc_(iter.pc()),
     frameNo_(iter.frameNo()),
-    numActualArgs_(numActualArgs),
+    numActualArgs_(iter.numActualArgs()),
     script_(iter.script())
 {
     CopyValueToRematerializedFrame op(slots_);
     iter.readFrameArgsAndLocals(cx, op, op, &scopeChain_, &returnValue_,
                                 &argsObj_, &thisValue_, ReadFrame_Actuals);
 }
 
 /* static */ RematerializedFrame *
 RematerializedFrame::New(ThreadSafeContext *cx, uint8_t *top, InlineFrameIterator &iter)
 {
     unsigned numFormals = iter.isFunctionFrame() ? iter.callee()->nargs() : 0;
-    unsigned numActualArgs = Max(numFormals, iter.numActualArgs());
     size_t numBytes = sizeof(RematerializedFrame) +
-        (numActualArgs + iter.script()->nfixed()) * sizeof(Value) -
+        (Max(numFormals, iter.numActualArgs()) +
+         iter.script()->nfixed()) * sizeof(Value) -
         sizeof(Value); // 1 Value included in sizeof(RematerializedFrame)
 
     void *buf = cx->calloc_(numBytes);
     if (!buf)
         return nullptr;
 
-    return new (buf) RematerializedFrame(cx, top, numActualArgs, iter);
-}
-
-/* static */ bool
-RematerializedFrame::RematerializeInlineFrames(ThreadSafeContext *cx, uint8_t *top,
-                                               InlineFrameIterator &iter,
-                                               Vector<RematerializedFrame *> &frames)
-{
-    if (!frames.resize(iter.frameCount()))
-        return false;
-
-    while (true) {
-        size_t frameNo = iter.frameNo();
-        frames[frameNo] = RematerializedFrame::New(cx, top, iter);
-        if (!frames[frameNo])
-            return false;
-
-        if (!iter.more())
-            break;
-        ++iter;
-    }
-
-    return true;
-}
-
-/* static */ void
-RematerializedFrame::FreeInVector(Vector<RematerializedFrame *> &frames)
-{
-    for (size_t i = 0; i < frames.length(); i++) {
-        RematerializedFrame *f = frames[i];
-        f->RematerializedFrame::~RematerializedFrame();
-        js_free(f);
-    }
-    frames.clear();
-}
-
-/* static */ void
-RematerializedFrame::MarkInVector(JSTracer *trc, Vector<RematerializedFrame *> &frames)
-{
-    for (size_t i = 0; i < frames.length(); i++)
-        frames[i]->mark(trc);
+    return new (buf) RematerializedFrame(cx, top, iter);
 }
 
 CallObject &
 RematerializedFrame::callObj() const
 {
     JS_ASSERT(hasCallObj());
 
     JSObject *scope = scopeChain();
@@ -118,46 +77,45 @@ RematerializedFrame::mark(JSTracer *trc)
     gc::MarkValueRoot(trc, &thisValue_, "remat ion frame this");
     gc::MarkValueRootRange(trc, slots_, slots_ + numActualArgs_ + script_->nfixed(),
                            "remat ion frame stack");
 }
 
 void
 RematerializedFrame::dump()
 {
-    fprintf(stderr, " Rematerialized Ion Frame%s\n", inlined() ? " (inlined)" : "");
+    fprintf(stderr, " Rematerialized Optimized Frame%s\n", inlined() ? " (inlined)" : "");
     if (isFunctionFrame()) {
         fprintf(stderr, "  callee fun: ");
 #ifdef DEBUG
-        js_DumpValue(ObjectValue(*callee()));
+        js_DumpObject(callee());
 #else
         fprintf(stderr, "?\n");
 #endif
     } else {
         fprintf(stderr, "  global frame, no callee\n");
     }
 
-    fprintf(stderr, "  file %s line %u offset %zu\n",
-            script()->filename(), (unsigned) script()->lineno(),
-            script()->pcToOffset(pc()));
+    fprintf(stderr, "  file %s line %u\n",
+            script()->filename(), (unsigned) script()->lineno());
 
     fprintf(stderr, "  script = %p\n", (void*) script());
 
     if (isFunctionFrame()) {
         fprintf(stderr, "  scope chain: ");
 #ifdef DEBUG
-        js_DumpValue(ObjectValue(*scopeChain()));
+        js_DumpObject(scopeChain());
 #else
         fprintf(stderr, "?\n");
 #endif
 
         if (hasArgsObj()) {
             fprintf(stderr, "  args obj: ");
 #ifdef DEBUG
-            js_DumpValue(ObjectValue(argsObj()));
+            js_DumpObject(&argsObj());
 #else
             fprintf(stderr, "?\n");
 #endif
         }
 
         fprintf(stderr, "  this: ");
 #ifdef DEBUG
         js_DumpValue(thisValue());
--- a/js/src/jit/RematerializedFrame.h
+++ b/js/src/jit/RematerializedFrame.h
@@ -25,63 +25,43 @@ namespace jit {
 class RematerializedFrame
 {
     // See DebugScopes::updateLiveScopes.
     bool prevUpToDate_;
 
     // The fp of the top frame associated with this possibly inlined frame.
     uint8_t *top_;
 
-    // The bytecode at the time of rematerialization.
-    jsbytecode *pc_;
-
     size_t frameNo_;
     unsigned numActualArgs_;
 
     JSScript *script_;
     JSObject *scopeChain_;
     ArgumentsObject *argsObj_;
 
     Value returnValue_;
     Value thisValue_;
     Value slots_[1];
 
-    RematerializedFrame(ThreadSafeContext *cx, uint8_t *top, unsigned numActualArgs,
-                        InlineFrameIterator &iter);
+    RematerializedFrame(ThreadSafeContext *cx, uint8_t *top, InlineFrameIterator &iter);
 
   public:
     static RematerializedFrame *New(ThreadSafeContext *cx, uint8_t *top,
                                     InlineFrameIterator &iter);
 
-    // Rematerialize all remaining frames pointed to by |iter| into |frames|
-    // in older-to-younger order, e.g., frames[0] is the oldest frame.
-    static bool RematerializeInlineFrames(ThreadSafeContext *cx, uint8_t *top,
-                                          InlineFrameIterator &iter,
-                                          Vector<RematerializedFrame *> &frames);
-
-    // Free a vector of RematerializedFrames; takes care to call the
-    // destructor. Also clears the vector.
-    static void FreeInVector(Vector<RematerializedFrame *> &frames);
-
-    // Mark a vector of RematerializedFrames.
-    static void MarkInVector(JSTracer *trc, Vector<RematerializedFrame *> &frames);
-
     bool prevUpToDate() const {
         return prevUpToDate_;
     }
     void setPrevUpToDate() {
         prevUpToDate_ = true;
     }
 
     uint8_t *top() const {
         return top_;
     }
-    jsbytecode *pc() const {
-        return pc_;
-    }
     size_t frameNo() const {
         return frameNo_;
     }
     bool inlined() const {
         return frameNo_ > 0;
     }
 
     JSObject *scopeChain() const {
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -173,26 +173,42 @@ CodeGeneratorARM::generateOutOfLineCode(
 
     if (deoptLabel_.used()) {
         // All non-table-based bailouts will go here.
         masm.bind(&deoptLabel_);
 
         // Push the frame size, so the handler can recover the IonScript.
         masm.ma_mov(Imm32(frameSize()), lr);
 
-        JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler(gen->info().executionMode());
+        JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
         masm.branch(handler);
     }
 
     return true;
 }
 
 bool
 CodeGeneratorARM::bailoutIf(Assembler::Condition condition, LSnapshot *snapshot)
 {
+    CompileInfo &info = snapshot->mir()->block()->info();
+    switch (info.executionMode()) {
+
+      case ParallelExecution: {
+        // in parallel mode, make no attempt to recover, just signal an error.
+        OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
+                                             snapshot->mir()->block(),
+                                             snapshot->mir()->pc());
+        masm.ma_b(ool->entry(), condition);
+        return true;
+      }
+      case SequentialExecution:
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("No such execution mode");
+    }
     if (!encode(snapshot))
         return false;
 
     // Though the assembler doesn't track all frame pushes, at least make sure
     // the known value makes sense. We can't use bailout tables if the stack
     // isn't properly aligned to the static frame size.
     JS_ASSERT_IF(frameClass_ != FrameSizeClass::None(),
                  frameClass_.frameSize() == masm.framePushed());
@@ -217,16 +233,33 @@ CodeGeneratorARM::bailoutIf(Assembler::C
 bool
 CodeGeneratorARM::bailoutFrom(Label *label, LSnapshot *snapshot)
 {
     if (masm.bailed())
         return false;
     JS_ASSERT(label->used());
     JS_ASSERT(!label->bound());
 
+    CompileInfo &info = snapshot->mir()->block()->info();
+    switch (info.executionMode()) {
+
+      case ParallelExecution: {
+        // in parallel mode, make no attempt to recover, just signal an error.
+        OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
+                                             snapshot->mir()->block(),
+                                             snapshot->mir()->pc());
+        masm.retarget(label, ool->entry());
+        return true;
+      }
+      case SequentialExecution:
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("No such execution mode");
+    }
+
     if (!encode(snapshot))
         return false;
 
     // Though the assembler doesn't track all frame pushes, at least make sure
     // the known value makes sense. We can't use bailout tables if the stack
     // isn't properly aligned to the static frame size.
     JS_ASSERT_IF(frameClass_ != FrameSizeClass::None(),
                  frameClass_.frameSize() == masm.framePushed());
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -63,20 +63,16 @@ class CodeGeneratorARM : public CodeGene
         masm.cmp32(lhs, rhs);
         return bailoutIf(c, snapshot);
     }
     template <typename T1, typename T2>
     bool bailoutTest32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot *snapshot) {
         masm.test32(lhs, rhs);
         return bailoutIf(c, snapshot);
     }
-    bool bailoutIfFalseBool(Register reg, LSnapshot *snapshot) {
-        masm.test32(reg, Imm32(0xFF));
-        return bailoutIf(Assembler::Zero, snapshot);
-    }
 
   protected:
     bool generatePrologue();
     bool generateAsmJSPrologue(Label *stackOverflowLabel);
     bool generateEpilogue();
     bool generateOutOfLineCode();
 
     void emitRoundDouble(FloatRegister src, Register dest, Label *fail);
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -11,17 +11,16 @@
 #include "jit/Bailouts.h"
 #include "jit/IonFrames.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
 #include "jit/JitCompartment.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
-#include "jit/ParallelFunctions.h"
 #include "jit/VMFunctions.h"
 
 #include "jit/ExecutionMode-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 static const FloatRegisterSet NonVolatileFloatRegs =
@@ -510,17 +509,17 @@ JitRuntime::generateArgumentsRectifier(J
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
 #endif
 
     return code;
 }
 
 static void
-PushBailoutFrame(MacroAssembler &masm, uint32_t frameClass, Register spArg)
+GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
 {
     // the stack should look like:
     // [IonFrame]
     // bailoutFrame.registersnapshot
     // bailoutFrame.fpsnapshot
     // bailoutFrame.snapshotOffset
     // bailoutFrame.frameSize
 
@@ -555,27 +554,20 @@ PushBailoutFrame(MacroAssembler &masm, u
     masm.startDataTransferM(IsStore, sp, DB, WriteBack);
     // set frameClassId_
     masm.transferReg(r4);
     // Set tableOffset_; higher registers are stored at higher locations on
     // the stack.
     masm.transferReg(lr);
     masm.finishDataTransfer();
 
-    masm.ma_mov(sp, spArg);
-}
-
-static void
-GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
-{
-    PushBailoutFrame(masm, frameClass, r0);
-
     // SP % 8 == 4
     // STEP 1c: Call the bailout function, giving a pointer to the
     //          structure we just blitted onto the stack
+    masm.ma_mov(sp, r0);
     const int sizeOfBailoutInfo = sizeof(void *)*2;
     masm.reserveStack(sizeOfBailoutInfo);
     masm.mov(sp, r1);
     masm.setupAlignedABICall(2);
 
     // Decrement sp by another 4, so we keep alignment
     // Not Anymore!  pushing both the snapshotoffset as well as the
     // masm.as_sub(sp, sp, Imm8(4));
@@ -613,42 +605,16 @@ GenerateBailoutThunk(JSContext *cx, Macr
                     , sp);
     }
 
     // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2.
     JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
     masm.branch(bailoutTail);
 }
 
-static void
-GenerateParallelBailoutThunk(MacroAssembler &masm, uint32_t frameClass)
-{
-    // As GenerateBailoutThunk, except we return an error immediately. We do
-    // the bailout dance so that we can walk the stack and have accurate
-    // reporting of frame information.
-
-    PushBailoutFrame(masm, frameClass, r0);
-
-    // Parallel bailout is like parallel failure in that we unwind all the way
-    // to the entry frame. Reserve space for the frame pointer of the entry frame.
-    const int sizeOfEntryFramePointer = sizeof(uint8_t *) * 2;
-    masm.reserveStack(sizeOfEntryFramePointer);
-    masm.mov(sp, r1);
-
-    masm.setupAlignedABICall(2);
-    masm.passABIArg(r0);
-    masm.passABIArg(r1);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, BailoutPar));
-
-    // Get the frame pointer of the entry fram and return.
-    masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
-    masm.ma_ldr(Address(sp, 0), sp);
-    masm.as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4)));
-}
-
 JitCode *
 JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
 {
     MacroAssembler masm(cx);
 
     Label bailout;
     for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++)
         masm.ma_bl(&bailout);
@@ -663,30 +629,20 @@ JitRuntime::generateBailoutTable(JSConte
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutTable");
 #endif
 
     return code;
 }
 
 JitCode *
-JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
+JitRuntime::generateBailoutHandler(JSContext *cx)
 {
     MacroAssembler masm(cx);
-
-    switch (mode) {
-      case SequentialExecution:
-        GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
-        break;
-      case ParallelExecution:
-        GenerateParallelBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID);
-        break;
-      default:
-        MOZ_ASSUME_UNREACHABLE("No such execution mode");
-    }
+    GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
     AutoFlushICache afc("BailoutHandler");
     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
 #endif
--- a/js/src/jit/mips/CodeGenerator-mips.cpp
+++ b/js/src/jit/mips/CodeGenerator-mips.cpp
@@ -208,16 +208,32 @@ CodeGeneratorMIPS::generateOutOfLineCode
 bool
 CodeGeneratorMIPS::bailoutFrom(Label *label, LSnapshot *snapshot)
 {
     if (masm.bailed())
         return false;
     MOZ_ASSERT(label->used());
     MOZ_ASSERT(!label->bound());
 
+    CompileInfo &info = snapshot->mir()->block()->info();
+    switch (info.executionMode()) {
+      case ParallelExecution: {
+        // in parallel mode, make no attempt to recover, just signal an error.
+        OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
+                                             snapshot->mir()->block(),
+                                             snapshot->mir()->pc());
+        masm.retarget(label, ool->entry());
+        return true;
+      }
+      case SequentialExecution:
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("No such execution mode");
+    }
+
     if (!encode(snapshot))
         return false;
 
     // Though the assembler doesn't track all frame pushes, at least make sure
     // the known value makes sense. We can't use bailout tables if the stack
     // isn't properly aligned to the static frame size.
     MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(),
                   frameClass_.frameSize() == masm.framePushed());
--- a/js/src/jit/mips/CodeGenerator-mips.h
+++ b/js/src/jit/mips/CodeGenerator-mips.h
@@ -88,21 +88,16 @@ class CodeGeneratorMIPS : public CodeGen
     bool bailoutCmpPtr(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot *snapshot) {
         return bailoutCmp32(c, lhs, rhs, snapshot);
     }
     bool bailoutTestPtr(Assembler::Condition c, Register lhs, Register rhs, LSnapshot *snapshot) {
         Label bail;
         masm.branchTestPtr(c, lhs, rhs, &bail);
         return bailoutFrom(&bail, snapshot);
     }
-    bool bailoutIfFalseBool(Register reg, LSnapshot *snapshot) {
-        Label bail;
-        masm.branchTest32(Assembler::Zero, reg, Imm32(0xFF), &bail);
-        return bailoutFrom(&bail, snapshot);
-    }
 
     bool bailoutFrom(Label *label, LSnapshot *snapshot);
     bool bailout(LSnapshot *snapshot);
 
   protected:
     bool generatePrologue();
     bool generateAsmJSPrologue(Label *stackOverflowLabel);
     bool generateEpilogue();
--- a/js/src/jit/mips/Trampoline-mips.cpp
+++ b/js/src/jit/mips/Trampoline-mips.cpp
@@ -637,30 +637,20 @@ JitRuntime::generateBailoutTable(JSConte
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutTable");
 #endif
 
     return code;
 }
 
 JitCode *
-JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
+JitRuntime::generateBailoutHandler(JSContext *cx)
 {
     MacroAssembler masm(cx);
-
-    switch (mode) {
-      case SequentialExecution:
-        GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
-        break;
-      case ParallelExecution:
-        GenerateParallelBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID);
-        break;
-      default:
-        MOZ_ASSUME_UNREACHABLE("No such execution mode");
-    }
+    GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
     AutoFlushICache afc("BailoutHandler");
     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
 #endif
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -335,23 +335,16 @@ bool
 CodeGeneratorShared::assignBailoutId(LSnapshot *snapshot)
 {
     JS_ASSERT(snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET);
 
     // Can we not use bailout tables at all?
     if (!deoptTable_)
         return false;
 
-    // We do not generate a bailout table for parallel code.
-    switch (gen->info().executionMode()) {
-      case SequentialExecution: break;
-      case ParallelExecution: return false;
-      default: MOZ_ASSUME_UNREACHABLE("No such execution mode");
-    }
-
     JS_ASSERT(frameClass_ != FrameSizeClass::None());
 
     if (snapshot->bailoutId() != INVALID_BAILOUT_ID)
         return true;
 
     // Is the bailout table full?
     if (bailouts_.length() >= BAILOUT_TABLE_SIZE)
         return false;
@@ -824,16 +817,133 @@ CodeGeneratorShared::markArgumentSlots(L
 {
     for (size_t i = 0; i < pushedArgumentSlots_.length(); i++) {
         if (!safepoint->addValueSlot(pushedArgumentSlots_[i]))
             return false;
     }
     return true;
 }
 
+OutOfLineAbortPar *
+CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock,
+                                 jsbytecode *bytecode)
+{
+    OutOfLineAbortPar *ool = new(alloc()) OutOfLineAbortPar(cause, basicBlock, bytecode);
+    if (!ool || !addOutOfLineCode(ool))
+        return nullptr;
+    return ool;
+}
+
+OutOfLineAbortPar *
+CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, LInstruction *lir)
+{
+    MDefinition *mir = lir->mirRaw();
+    MBasicBlock *block = mir->block();
+    jsbytecode *pc = mir->trackedPc();
+    if (!pc) {
+        if (lir->snapshot())
+            pc = lir->snapshot()->mir()->pc();
+        else
+            pc = block->pc();
+    }
+    return oolAbortPar(cause, block, pc);
+}
+
+OutOfLinePropagateAbortPar *
+CodeGeneratorShared::oolPropagateAbortPar(LInstruction *lir)
+{
+    OutOfLinePropagateAbortPar *ool = new(alloc()) OutOfLinePropagateAbortPar(lir);
+    if (!ool || !addOutOfLineCode(ool))
+        return nullptr;
+    return ool;
+}
+
+bool
+OutOfLineAbortPar::generate(CodeGeneratorShared *codegen)
+{
+    codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar");
+    return codegen->visitOutOfLineAbortPar(this);
+}
+
+bool
+OutOfLinePropagateAbortPar::generate(CodeGeneratorShared *codegen)
+{
+    codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar");
+    return codegen->visitOutOfLinePropagateAbortPar(this);
+}
+
+bool
+CodeGeneratorShared::callTraceLIR(uint32_t blockIndex, LInstruction *lir,
+                                  const char *bailoutName)
+{
+    JS_ASSERT_IF(!lir, bailoutName);
+
+    if (!IonSpewEnabled(IonSpew_Trace))
+        return true;
+
+    uint32_t execMode = (uint32_t) gen->info().executionMode();
+    uint32_t lirIndex;
+    const char *lirOpName;
+    const char *mirOpName;
+    JSScript *script;
+    jsbytecode *pc;
+
+    masm.PushRegsInMask(RegisterSet::Volatile());
+    masm.reserveStack(sizeof(IonLIRTraceData));
+
+    // This first move is here so that when you scan the disassembly,
+    // you can easily pick out where each instruction begins.  The
+    // next few items indicate to you the Basic Block / LIR.
+    masm.move32(Imm32(0xDEADBEEF), CallTempReg0);
+
+    if (lir) {
+        lirIndex = lir->id();
+        lirOpName = lir->opName();
+        if (MDefinition *mir = lir->mirRaw()) {
+            mirOpName = mir->opName();
+            script = mir->block()->info().script();
+            pc = mir->trackedPc();
+        } else {
+            mirOpName = nullptr;
+            script = nullptr;
+            pc = nullptr;
+        }
+    } else {
+        blockIndex = lirIndex = 0xDEADBEEF;
+        lirOpName = mirOpName = bailoutName;
+        script = nullptr;
+        pc = nullptr;
+    }
+
+    masm.store32(Imm32(blockIndex),
+                 Address(StackPointer, offsetof(IonLIRTraceData, blockIndex)));
+    masm.store32(Imm32(lirIndex),
+                 Address(StackPointer, offsetof(IonLIRTraceData, lirIndex)));
+    masm.store32(Imm32(execMode),
+                 Address(StackPointer, offsetof(IonLIRTraceData, execModeInt)));
+    masm.storePtr(ImmPtr(lirOpName),
+                  Address(StackPointer, offsetof(IonLIRTraceData, lirOpName)));
+    masm.storePtr(ImmPtr(mirOpName),
+                  Address(StackPointer, offsetof(IonLIRTraceData, mirOpName)));
+    masm.storePtr(ImmGCPtr(script),
+                  Address(StackPointer, offsetof(IonLIRTraceData, script)));
+    masm.storePtr(ImmPtr(pc),
+                  Address(StackPointer, offsetof(IonLIRTraceData, pc)));
+
+    masm.movePtr(StackPointer, CallTempReg0);
+    masm.setupUnalignedABICall(1, CallTempReg1);
+    masm.passABIArg(CallTempReg0);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLIR));
+
+    masm.freeStack(sizeof(IonLIRTraceData));
+    masm.PopRegsInMask(RegisterSet::Volatile());
+
+    return true;
+}
+
 Label *
 CodeGeneratorShared::labelForBackedgeWithImplicitCheck(MBasicBlock *mir)
 {
     // If this is a loop backedge to a loop header with an implicit interrupt
     // check, use a patchable jump. Skip this search if compiling without a
     // script for asm.js, as there will be no interrupt check instruction.
     // Due to critical edge unsplitting there may no longer be unique loop
     // backedges, so just look for any edge going to an earlier block in RPO.
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -21,16 +21,18 @@
 
 namespace js {
 namespace jit {
 
 class OutOfLineCode;
 class CodeGenerator;
 class MacroAssembler;
 class IonCache;
+class OutOfLineAbortPar;
+class OutOfLinePropagateAbortPar;
 
 template <class ArgSeq, class StoreOutputTo>
 class OutOfLineCallVM;
 
 class OutOfLineTruncateSlow;
 
 struct PatchableBackedgeInfo
 {
@@ -461,16 +463,38 @@ class CodeGeneratorShared : public LInst
   public:
     template <class ArgSeq, class StoreOutputTo>
     bool visitOutOfLineCallVM(OutOfLineCallVM<ArgSeq, StoreOutputTo> *ool);
 
     bool visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool);
 
     bool omitOverRecursedCheck() const;
 
+  public:
+    bool callTraceLIR(uint32_t blockIndex, LInstruction *lir, const char *bailoutName = nullptr);
+
+    // Parallel aborts:
+    //
+    //    Parallel aborts work somewhat differently from sequential
+    //    bailouts.  When an abort occurs, we first invoke
+    //    ReportAbortPar() and then we return JS_ION_ERROR.  Each
+    //    call on the stack will check for this error return and
+    //    propagate it upwards until the C++ code that invoked the ion
+    //    code is reached.
+    //
+    //    The snapshot that is provided to `oolAbortPar` is currently
+    //    only used for error reporting, so that we can provide feedback
+    //    to the user about which instruction aborted and (perhaps) why.
+    OutOfLineAbortPar *oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock,
+                                   jsbytecode *bytecode);
+    OutOfLineAbortPar *oolAbortPar(ParallelBailoutCause cause, LInstruction *lir);
+    OutOfLinePropagateAbortPar *oolPropagateAbortPar(LInstruction *lir);
+    virtual bool visitOutOfLineAbortPar(OutOfLineAbortPar *ool) = 0;
+    virtual bool visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool) = 0;
+
 #ifdef JS_TRACE_LOGGING
   protected:
     bool emitTracelogScript(bool isStart);
     bool emitTracelogTree(bool isStart, uint32_t textId);
 
   public:
     bool emitTracelogScriptStart() {
         return emitTracelogScript(/* isStart =*/ true);
@@ -741,12 +765,59 @@ CodeGeneratorShared::visitOutOfLineCallV
     if (!callVM(ool->function(), lir))
         return false;
     ool->out().generate(this);
     restoreLiveIgnore(lir, ool->out().clobbered());
     masm.jump(ool->rejoin());
     return true;
 }
 
+// Initiate a parallel abort.  The snapshot is used to record the
+// cause.
+class OutOfLineAbortPar : public OutOfLineCode
+{
+  private:
+    ParallelBailoutCause cause_;
+    MBasicBlock *basicBlock_;
+    jsbytecode *bytecode_;
+
+  public:
+    OutOfLineAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock, jsbytecode *bytecode)
+      : cause_(cause),
+        basicBlock_(basicBlock),
+        bytecode_(bytecode)
+    { }
+
+    ParallelBailoutCause cause() {
+        return cause_;
+    }
+
+    MBasicBlock *basicBlock() {
+        return basicBlock_;
+    }
+
+    jsbytecode *bytecode() {
+        return bytecode_;
+    }
+
+    bool generate(CodeGeneratorShared *codegen);
+};
+
+// Used when some callee has aborted.
+class OutOfLinePropagateAbortPar : public OutOfLineCode
+{
+  private:
+    LInstruction *lir_;
+
+  public:
+    explicit OutOfLinePropagateAbortPar(LInstruction *lir)
+      : lir_(lir)
+    { }
+
+    LInstruction *lir() { return lir_; }
+
+    bool generate(CodeGeneratorShared *codegen);
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_CodeGenerator_shared_h */
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -351,17 +351,17 @@ CodeGeneratorX86Shared::generateOutOfLin
 
     if (deoptLabel_.used()) {
         // All non-table-based bailouts will go here.
         masm.bind(&deoptLabel_);
 
         // Push the frame size, so the handler can recover the IonScript.
         masm.push(Imm32(frameSize()));
 
-        JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler(gen->info().executionMode());
+        JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
         masm.jmp(ImmPtr(handler->raw()), Relocation::JITCODE);
     }
 
     return true;
 }
 
 class BailoutJump {
     Assembler::Condition cond_;
@@ -393,16 +393,32 @@ class BailoutLabel {
     void operator()(MacroAssembler &masm, Label *label) const {
         masm.retarget(label_, label);
     }
 };
 
 template <typename T> bool
 CodeGeneratorX86Shared::bailout(const T &binder, LSnapshot *snapshot)
 {
+    CompileInfo &info = snapshot->mir()->block()->info();
+    switch (info.executionMode()) {
+      case ParallelExecution: {
+        // in parallel mode, make no attempt to recover, just signal an error.
+        OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
+                                             snapshot->mir()->block(),
+                                             snapshot->mir()->pc());
+        binder(masm, ool->entry());
+        return true;
+      }
+      case SequentialExecution:
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("No such execution mode");
+    }
+
     if (!encode(snapshot))
         return false;
 
     // Though the assembler doesn't track all frame pushes, at least make sure
     // the known value makes sense. We can't use bailout tables if the stack
     // isn't properly aligned to the static frame size.
     JS_ASSERT_IF(frameClass_ != FrameSizeClass::None() && deoptTable_,
                  frameClass_.frameSize() == masm.framePushed());
--- a/js/src/jit/shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.h
@@ -70,20 +70,16 @@ class CodeGeneratorX86Shared : public Co
         masm.cmp32(lhs, rhs);
         return bailoutIf(c, snapshot);
     }
     template <typename T1, typename T2>
     bool bailoutTest32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot *snapshot) {
         masm.test32(lhs, rhs);
         return bailoutIf(c, snapshot);
     }
-    bool bailoutIfFalseBool(Register reg, LSnapshot *snapshot) {
-        masm.test32(reg, Imm32(0xFF));
-        return bailoutIf(Assembler::Zero, snapshot);
-    }
     bool bailoutCvttsd2si(FloatRegister src, Register dest, LSnapshot *snapshot) {
         // cvttsd2si returns 0x80000000 on failure. Test for it by
         // subtracting 1 and testing overflow. The other possibility is to test
         // equality for INT_MIN after a comparison, but 1 costs fewer bytes to
         // materialize.
         masm.cvttsd2si(src, dest);
         masm.cmp32(dest, Imm32(1));
         return bailoutIf(Assembler::Overflow, snapshot);
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -6,17 +6,16 @@
 
 #include "jit/Bailouts.h"
 #include "jit/IonFrames.h"
 #include "jit/IonLinker.h"
 #include "jit/JitCompartment.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
-#include "jit/ParallelFunctions.h"
 #include "jit/VMFunctions.h"
 #include "jit/x64/BaselineHelpers-x64.h"
 
 using namespace js;
 using namespace js::jit;
 
 // All registers to save and restore. This includes the stack pointer, since we
 // use the ability to reference register values on the stack by index.
@@ -442,29 +441,23 @@ JitRuntime::generateArgumentsRectifier(J
     CodeOffsetLabel returnLabel(returnOffset);
     returnLabel.fixup(&masm);
     if (returnAddrOut)
         *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
     return code;
 }
 
 static void
-PushBailoutFrame(MacroAssembler &masm, Register spArg)
+GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
 {
     // Push registers such that we can access them from [base + code].
     masm.PushRegsInMask(AllRegs);
 
     // Get the stack pointer into a register, pre-alignment.
-    masm.movq(rsp, spArg);
-}
-
-static void
-GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
-{
-    PushBailoutFrame(masm, r8);
+    masm.movq(rsp, r8);
 
     // Make space for Bailout's bailoutInfo outparam.
     masm.reserveStack(sizeof(void *));
     masm.movq(rsp, r9);
 
     // Call the bailout function.
     masm.setupUnalignedABICall(2, rax);
     masm.passABIArg(r8);
@@ -476,72 +469,38 @@ GenerateBailoutThunk(JSContext *cx, Macr
     // Stack is:
     //     [frame]
     //     snapshotOffset
     //     frameSize
     //     [bailoutFrame]
     //
     // Remove both the bailout frame and the topmost Ion frame's stack.
     static const uint32_t BailoutDataSize = sizeof(void *) * Registers::Total +
-                                            sizeof(double) * FloatRegisters::Total;
+                                          sizeof(double) * FloatRegisters::Total;
     masm.addq(Imm32(BailoutDataSize), rsp);
     masm.pop(rcx);
     masm.lea(Operand(rsp, rcx, TimesOne, sizeof(void *)), rsp);
 
     // Jump to shared bailout tail. The BailoutInfo pointer has to be in r9.
     JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
     masm.jmp(bailoutTail);
 }
 
-static void
-GenerateParallelBailoutThunk(MacroAssembler &masm)
-{
-    // As GenerateBailoutThunk, except we return an error immediately. We do
-    // the bailout dance so that we can walk the stack and have accurate
-    // reporting of frame information.
-
-    PushBailoutFrame(masm, r8);
-
-    // Parallel bailout is like parallel failure in that we unwind all the way
-    // to the entry frame. Reserve space for the frame pointer of the entry frame.
-    masm.reserveStack(sizeof(uint8_t *));
-    masm.movePtr(rsp, r9);
-
-    masm.setupUnalignedABICall(2, rax);
-    masm.passABIArg(r8);
-    masm.passABIArg(r9);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, BailoutPar));
-
-    // Get the frame pointer of the entry frame and return.
-    masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
-    masm.loadPtr(Address(rsp, 0), rsp);
-    masm.ret();
-}
-
 JitCode *
 JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
 {
     MOZ_ASSUME_UNREACHABLE("x64 does not use bailout tables");
 }
 
 JitCode *
-JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
+JitRuntime::generateBailoutHandler(JSContext *cx)
 {
     MacroAssembler masm;
 
-    switch (mode) {
-      case SequentialExecution:
-        GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
-        break;
-      case ParallelExecution:
-        GenerateParallelBailoutThunk(masm);
-        break;
-      default:
-        MOZ_ASSUME_UNREACHABLE("No such execution mode");
-    }
+    GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
 #endif
 
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -11,17 +11,16 @@
 #include "jit/BaselineJIT.h"
 #include "jit/IonFrames.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
 #include "jit/JitCompartment.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
-#include "jit/ParallelFunctions.h"
 #include "jit/VMFunctions.h"
 #include "jit/x86/BaselineHelpers-x86.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/ExecutionMode-inl.h"
 
 using namespace js;
@@ -451,32 +450,26 @@ JitRuntime::generateArgumentsRectifier(J
     CodeOffsetLabel returnLabel(returnOffset);
     returnLabel.fixup(&masm);
     if (returnAddrOut)
         *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
     return code;
 }
 
 static void
-PushBailoutFrame(MacroAssembler &masm, uint32_t frameClass, Register spArg)
+GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
 {
     // Push registers such that we can access them from [base + code].
     masm.PushRegsInMask(AllRegs);
 
     // Push the bailout table number.
     masm.push(Imm32(frameClass));
 
     // The current stack pointer is the first argument to jit::Bailout.
-    masm.movl(esp, spArg);
-}
-
-static void
-GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
-{
-    PushBailoutFrame(masm, frameClass, eax);
+    masm.movl(esp, eax);
 
     // Make space for Bailout's baioutInfo outparam.
     masm.reserveStack(sizeof(void *));
     masm.movl(esp, ebx);
 
     // Call the bailout function. This will correct the size of the bailout.
     masm.setupUnalignedABICall(2, ecx);
     masm.passABIArg(eax);
@@ -510,41 +503,16 @@ GenerateBailoutThunk(JSContext *cx, Macr
         masm.addl(Imm32(BailoutDataSize + sizeof(void *) + frameSize), esp);
     }
 
     // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx.
     JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
     masm.jmp(bailoutTail);
 }
 
-static void
-GenerateParallelBailoutThunk(MacroAssembler &masm, uint32_t frameClass)
-{
-    // As GenerateBailoutThunk, except we return an error immediately. We do
-    // the bailout dance so that we can walk the stack and have accurate
-    // reporting of frame information.
-
-    PushBailoutFrame(masm, frameClass, eax);
-
-    // Parallel bailout is like parallel failure in that we unwind all the way
-    // to the entry frame. Reserve space for the frame pointer of the entry frame.
-    masm.reserveStack(sizeof(uint8_t *));
-    masm.movePtr(esp, ebx);
-
-    masm.setupUnalignedABICall(2, ecx);
-    masm.passABIArg(eax);
-    masm.passABIArg(ebx);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, BailoutPar));
-
-    // Get the frame pointer of the entry frame and return.
-    masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
-    masm.loadPtr(Address(esp, 0), esp);
-    masm.ret();
-}
-
 JitCode *
 JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
 {
     MacroAssembler masm;
 
     Label bailout;
     for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++)
         masm.call(&bailout);
@@ -558,30 +526,21 @@ JitRuntime::generateBailoutTable(JSConte
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
 #endif
 
     return code;
 }
 
 JitCode *
-JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
+JitRuntime::generateBailoutHandler(JSContext *cx)
 {
     MacroAssembler masm;
 
-    switch (mode) {
-      case SequentialExecution:
-        GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
-        break;
-      case ParallelExecution:
-        GenerateParallelBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID);
-        break;
-      default:
-        MOZ_ASSUME_UNREACHABLE("No such execution mode");
-    }
+    GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
 #endif
 
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -22,17 +22,16 @@
 
 #ifdef JS_THREADSAFE
 # include "jit/BaselineJIT.h"
 # include "vm/Monitor.h"
 #endif
 
 #if defined(JS_THREADSAFE) && defined(JS_ION)
 # include "jit/JitCommon.h"
-# include "jit/RematerializedFrame.h"
 # ifdef FORKJOIN_SPEW
 #  include "jit/Ion.h"
 #  include "jit/JitCompartment.h"
 #  include "jit/MIR.h"
 #  include "jit/MIRGraph.h"
 # endif
 #endif // THREADSAFE && ION
 
@@ -113,23 +112,36 @@ ForkJoinContext::requestZoneGC(JS::Zone 
 bool
 ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
 {
     MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
     return false;
 }
 
 void
-ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, JitFrameIterator &frameIter)
+ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
+                                JSScript *outermostScript,
+                                JSScript *currentScript,
+                                jsbytecode *currentPc)
 {
     MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
 }
 
 void
-ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, IonBailoutIterator &frameIter)
+js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
+                                       JSScript *outermostScript,
+                                       JSScript *currentScript,
+                                       jsbytecode *currentPc)
+{
+    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
+}
+
+void
+ParallelBailoutRecord::addTrace(JSScript *script,
+                                jsbytecode *pc)
 {
     MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
 }
 
 bool
 js::InExclusiveParallelSection()
 {
     return false;
@@ -257,16 +269,21 @@ enum ForkJoinMode {
 
 class ForkJoinOperation
 {
   public:
     // For tests, make sure to keep this in sync with minItemsTestingThreshold.
     static const uint32_t MAX_BAILOUTS = 3;
     uint32_t bailouts;
 
+    // Information about the bailout:
+    ParallelBailoutCause bailoutCause;
+    RootedScript bailoutScript;
+    jsbytecode *bailoutBytecode;
+
     ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
                       uint16_t sliceEnd, ForkJoinMode mode, HandleObject updatable);
     ExecutionStatus apply();
 
   private:
     // Most of the functions involved in managing the parallel
     // compilation follow a similar control-flow. They return RedLight
     // if they have either encountered a fatal error or completed the
@@ -300,32 +317,30 @@ class ForkJoinOperation
         }
     };
 
     JSContext *cx_;
     HandleFunction fun_;
     HandleObject updatable_;
     uint16_t sliceStart_;
     uint16_t sliceEnd_;
-    Vector<ParallelBailoutRecord> bailoutRecords_;
+    Vector<ParallelBailoutRecord, 16> bailoutRecords_;
     AutoScriptVector worklist_;
     Vector<WorklistData, 16> worklistData_;
     ForkJoinMode mode_;
 
     TrafficLight enqueueInitialScript(ExecutionStatus *status);
     TrafficLight compileForParallelExecution(ExecutionStatus *status);
     TrafficLight warmupExecution(bool stopIfComplete, ExecutionStatus *status);
     TrafficLight parallelExecution(ExecutionStatus *status);
     TrafficLight sequentialExecution(bool disqualified, ExecutionStatus *status);
     TrafficLight recoverFromBailout(ExecutionStatus *status);
     TrafficLight fatalError(ExecutionStatus *status);
     bool isInitialScript(HandleScript script);
-    void getBailoutScriptAndPc(MutableHandleScript bailoutScript, jsbytecode **bailoutPc,
-                               ParallelBailoutCause *bailoutCause);
-    bool reportBailoutWarnings();
+    void determineBailoutCause();
     bool invalidateBailedOutScripts();
     ExecutionStatus sequentialExecution(bool disqualified);
 
     TrafficLight appendCallTargetsToWorklist(uint32_t index, ExecutionStatus *status);
     TrafficLight appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status);
     bool addToWorklist(HandleScript script);
     inline bool hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script);
 }; // class ForkJoinOperation
@@ -334,24 +349,24 @@ class ForkJoinShared : public ParallelJo
 {
 #ifdef JSGC_FJGENERATIONAL
     friend class gc::ForkJoinGCShared;
 #endif
 
     /////////////////////////////////////////////////////////////////////////
     // Constant fields
 
-    JSContext *const cx_;                    // Current context
-    ThreadPool *const threadPool_;           // The thread pool
-    HandleFunction fun_;                     // The JavaScript function to execute
-    HandleObject updatable_;                 // Pre-existing object that might be updated
-    uint16_t sliceStart_;                    // The starting slice id.
-    uint16_t sliceEnd_;                      // The ending slice id + 1.
-    PRLock *cxLock_;                         // Locks cx_ for parallel VM calls
-    Vector<ParallelBailoutRecord> &records_; // Bailout records for each worker
+    JSContext *const cx_;                  // Current context
+    ThreadPool *const threadPool_;         // The thread pool
+    HandleFunction fun_;                   // The JavaScript function to execute
+    HandleObject updatable_;               // Pre-existing object that might be updated
+    uint16_t sliceStart_;                  // The starting slice id.
+    uint16_t sliceEnd_;                    // The ending slice id + 1.
+    PRLock *cxLock_;                       // Locks cx_ for parallel VM calls
+    ParallelBailoutRecord *const records_; // Bailout records for each worker
 
     /////////////////////////////////////////////////////////////////////////
     // Per-thread arenas
     //
     // Each worker thread gets an arena to use when allocating.
 
     Vector<Allocator *, 16> allocators_;
 
@@ -377,17 +392,17 @@ class ForkJoinShared : public ParallelJo
 
   public:
     ForkJoinShared(JSContext *cx,
                    ThreadPool *threadPool,
                    HandleFunction fun,
                    HandleObject updatable,
                    uint16_t sliceStart,
                    uint16_t sliceEnd,
-                   Vector<ParallelBailoutRecord> &records);
+                   ParallelBailoutRecord *records);
     ~ForkJoinShared();
 
     bool init();
 
     ParallelResult execute();
 
     // Invoked from parallel worker threads:
     virtual bool executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit) MOZ_OVERRIDE;
@@ -572,16 +587,19 @@ ForkJoinModeString(ForkJoinMode mode) {
       case NumForkJoinModes: return "max";
     }
     return "???";
 }
 
 ForkJoinOperation::ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
                                      uint16_t sliceEnd, ForkJoinMode mode, HandleObject updatable)
   : bailouts(0),
+    bailoutCause(ParallelBailoutNone),
+    bailoutScript(cx),
+    bailoutBytecode(nullptr),
     cx_(cx),
     fun_(fun),
     updatable_(updatable),
     sliceStart_(sliceStart),
     sliceEnd_(sliceEnd),
     bailoutRecords_(cx),
     worklist_(cx),
     worklistData_(cx),
@@ -622,20 +640,18 @@ ForkJoinOperation::apply()
     SpewBeginOp(cx_, "ForkJoinOperation");
 
     // How many workers do we have, counting the main thread.
     unsigned numWorkers = cx_->runtime()->threadPool.numWorkers();
 
     if (!bailoutRecords_.resize(numWorkers))
         return SpewEndOp(ExecutionFatal);
 
-    for (uint32_t i = 0; i < numWorkers; i++) {
-        if (!bailoutRecords_[i].init(cx_))
-            return SpewEndOp(ExecutionFatal);
-    }
+    for (uint32_t i = 0; i < numWorkers; i++)
+        bailoutRecords_[i].init(cx_);
 
     if (enqueueInitialScript(&status) == RedLight)
         return SpewEndOp(status);
 
     Spew(SpewOps, "Execution mode: %s", ForkJoinModeString(mode_));
     switch (mode_) {
       case ForkJoinModeNormal:
       case ForkJoinModeCompile:
@@ -655,17 +671,17 @@ ForkJoinOperation::apply()
         break;
 
       case NumForkJoinModes:
         MOZ_ASSUME_UNREACHABLE("Invalid mode");
     }
 
     while (bailouts < MAX_BAILOUTS) {
         for (uint32_t i = 0; i < numWorkers; i++)
-            bailoutRecords_[i].reset();
+            bailoutRecords_[i].reset(cx_);
 
         if (compileForParallelExecution(&status) == RedLight)
             return SpewEndOp(status);
 
         JS_ASSERT(worklist_.length() == 0);
         if (parallelExecution(&status) == RedLight)
             return SpewEndOp(status);
 
@@ -1082,195 +1098,96 @@ BailoutExplanation(ParallelBailoutCause 
 
 bool
 ForkJoinOperation::isInitialScript(HandleScript script)
 {
     return fun_->is<JSFunction>() && (fun_->as<JSFunction>().nonLazyScript() == script);
 }
 
 void
-ForkJoinOperation::getBailoutScriptAndPc(MutableHandleScript bailoutScript, jsbytecode **bailoutPc,
-                                         ParallelBailoutCause *bailoutCause)
-{
-    for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
-        switch (bailoutRecords_[i].cause) {
-          case ParallelBailoutNone:
-          case ParallelBailoutInterrupt:
-            continue;
-          default:
-            break;
-        }
-
-        if (bailoutRecords_[i].hasFrames()) {
-            RematerializedFrame *frame = bailoutRecords_[i].frames()[0];
-            bailoutScript.set(frame->script());
-            *bailoutPc = frame->pc();
-            *bailoutCause = bailoutRecords_[i].cause;
-            return;
-        }
-    }
-}
-
-static const char *
-ValueToChar(JSContext *cx, HandleValue val, JSAutoByteString &bytes)
+ForkJoinOperation::determineBailoutCause()
 {
-    if (val.isMagic()) {
-        switch (val.whyMagic()) {
-          case JS_OPTIMIZED_OUT: return "<optimized out>";
-          default: return "<unknown magic?>";
-        }
-    }
-
-    RootedString str(cx, ToString<CanGC>(cx, val));
-    if (!str)
-        return nullptr;
-    return bytes.encodeUtf8(cx, str);
-}
+    bailoutCause = ParallelBailoutNone;
+    for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
+        if (bailoutRecords_[i].cause == ParallelBailoutNone)
+            continue;
 
-bool
-ForkJoinOperation::reportBailoutWarnings()
-{
-    Sprinter sp(cx_);
-    if (SpewEnabled(SpewBailouts)) {
-        sp.init();
-        sp.printf("Bailed out of parallel operation");
-    }
-
-    for (uint32_t threadId = 0; threadId < bailoutRecords_.length(); threadId++) {
-        ParallelBailoutCause cause = bailoutRecords_[threadId].cause;
-        if (cause == ParallelBailoutNone)
+        if (bailoutRecords_[i].cause == ParallelBailoutInterrupt)
             continue;
 
-        if (bailoutRecords_[threadId].hasFrames()) {
-            Vector<RematerializedFrame *> &frames = bailoutRecords_[threadId].frames();
-
-            if (!SpewEnabled(SpewBailouts)) {
-                RematerializedFrame *frame = frames[0];
-                RootedScript bailoutScript(cx_, frame->script());
-                SpewBailout(bailouts, bailoutScript, frame->pc(), cause);
-                JS_ReportWarning(cx_, "Bailed out of parallel operation: %s at %s:%u",
-                                 BailoutExplanation(cause), bailoutScript->filename(),
-                                 PCToLineNumber(bailoutScript, frame->pc()));
-                return true;
-            }
-
-            sp.printf("\n  in thread %d due to %s", threadId, BailoutExplanation(cause));
+        bailoutCause = bailoutRecords_[i].cause;
+        const char *causeStr = BailoutExplanation(bailoutCause);
+        if (bailoutRecords_[i].depth) {
+            bailoutScript = bailoutRecords_[i].trace[0].script;
+            bailoutBytecode = bailoutRecords_[i].trace[0].bytecode;
 
-            for (uint32_t frameIndex = 0; frameIndex < frames.length(); frameIndex++) {
-                RematerializedFrame *frame = frames[frameIndex];
-                RootedScript script(cx_, frame->script());
-
-                // Format the frame's location.
-                sp.printf("\n    at ");
-                if (JSFunction *fun = frame->maybeFun()) {
-                    if (fun->displayAtom()) {
-                        JSAutoByteString displayBytes;
-                        RootedString displayAtom(cx_, fun->displayAtom());
-                        const char *displayChars = displayBytes.encodeUtf8(cx_, displayAtom);
-                        if (!displayChars)
-                            return false;
-                        sp.printf("%s", displayChars);
-                    } else {
-                        sp.printf("<anonymous>");
-                    }
-                } else {
-                    sp.printf("<global>");
-                }
-                sp.printf(" (%s:%u)", script->filename(), PCToLineNumber(script, frame->pc()));
+            const char *filename = bailoutScript->filename();
+            int line = JS_PCToLineNumber(cx_, bailoutScript, bailoutBytecode);
+            JS_ReportWarning(cx_, "Bailed out of parallel operation: %s at %s:%d",
+                             causeStr, filename, line);
 
-                // Format bindings.
-                BindingVector bindings(cx_);
-                if (!FillBindingVector(script, &bindings))
-                    return false;
-
-                unsigned scopeSlot = 0;
-                for (unsigned i = 0; i < bindings.length(); i++) {
-                    JSAutoByteString nameBytes;
-                    const char *nameChars = nullptr;
-                    RootedPropertyName bindingName(cx_, bindings[i].name());
-                    nameChars = nameBytes.encodeUtf8(cx_, bindingName);
-                    if (!nameChars)
-                        return false;
+            Spew(SpewBailouts, "Bailout from thread %d: cause %d at loc %s:%d",
+                 i,
+                 bailoutCause,
+                 bailoutScript->filename(),
+                 PCToLineNumber(bailoutScript, bailoutBytecode));
+        } else {
+            JS_ReportWarning(cx_, "Bailed out of parallel operation: %s",
+                             causeStr);
 
-                    RootedValue arg(cx_);
-                    if (bindings[i].aliased()) {
-                        arg = frame->callObj().getSlot(scopeSlot);
-                        scopeSlot++;
-                    } else if (i < frame->numFormalArgs()) {
-                        if (script->argsObjAliasesFormals() && frame->hasArgsObj())
-                            arg = frame->argsObj().arg(i);
-                        else
-                            arg = frame->unaliasedActual(i, DONT_CHECK_ALIASING);
-                    } else {
-                        arg = frame->unaliasedLocal(i - frame->numFormalArgs(), DONT_CHECK_ALIASING);
-                    }
-
-                    JSAutoByteString valueBytes;
-                    const char *valueChars = ValueToChar(cx_, arg, valueBytes);
-                    if (!valueChars)
-                        return false;
-
-                    sp.printf("\n      %s %s = %s",
-                              bindings[i].kind() == Binding::ARGUMENT ? "arg" : "var",
-                              nameChars, valueChars);
-                }
-            }
+            Spew(SpewBailouts, "Bailout from thread %d: cause %d, unknown loc",
+                 i,
+                 bailoutCause);
         }
     }
-
-    if (SpewEnabled(SpewBailouts))
-        JS_ReportWarning(cx_, "%s", sp.string());
-
-    return true;
 }
 
 bool
 ForkJoinOperation::invalidateBailedOutScripts()
 {
     Vector<types::RecompileInfo> invalid(cx_);
     for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
-        switch (bailoutRecords_[i].cause) {
-          // No bailout.
-          case ParallelBailoutNone:
-            continue;
+        RootedScript script(cx_, bailoutRecords_[i].topScript);
 
-          // An interrupt is not the fault of the script, so don't
-          // invalidate it.
-          case ParallelBailoutInterrupt:
-            continue;
-
-          // An illegal write will not be made legal by invalidation.
-          case ParallelBailoutIllegalWrite:
+        // No script to invalidate.
+        if (!script || !script->hasParallelIonScript())
             continue;
 
+        Spew(SpewBailouts,
+             "Bailout from thread %d: cause %d, topScript %p:%s:%d",
+             i,
+             bailoutRecords_[i].cause,
+             script.get(), script->filename(), script->lineno());
+
+        switch (bailoutRecords_[i].cause) {
+          // An interrupt is not the fault of the script, so don't
+          // invalidate it.
+          case ParallelBailoutInterrupt: continue;
+
+          // An illegal write will not be made legal by invalidation.
+          case ParallelBailoutIllegalWrite: continue;
+
           // For other cases, consider invalidation.
-          default:
-            break;
+          default: break;
         }
 
-        if (!bailoutRecords_[i].hasFrames())
-            continue;
-
-        // Get the script of the youngest frame.
-        RootedScript script(cx_, bailoutRecords_[i].frames()[0]->script());
-
         // Already invalidated.
-        if (!script->hasParallelIonScript() || hasScript(invalid, script))
+        if (hasScript(invalid, script))
             continue;
 
         Spew(SpewBailouts, "Invalidating script %p:%s:%d due to cause %d",
              script.get(), script->filename(), script->lineno(),
              bailoutRecords_[i].cause);
 
         types::RecompileInfo co = script->parallelIonScript()->recompileInfo();
 
         if (!invalid.append(co))
             return false;
 
-        // Any script that we have marked for invalidation will need
+        // any script that we have marked for invalidation will need
         // to be recompiled
         if (!addToWorklist(script))
             return false;
     }
 
     Invalidate(cx_, invalid);
 
     return true;
@@ -1329,17 +1246,17 @@ ForkJoinOperation::parallelExecution(Exe
         Spew(SpewOps, "Warmup execution finished all the work.");
         *status = ExecutionWarmup;
         return RedLight;
     }
 
     ForkJoinActivation activation(cx_);
     ThreadPool *threadPool = &cx_->runtime()->threadPool;
     ForkJoinShared shared(cx_, threadPool, fun_, updatable_, sliceStart_, sliceEnd_,
-                          bailoutRecords_);
+                          &bailoutRecords_[0]);
     if (!shared.init()) {
         *status = ExecutionFatal;
         return RedLight;
     }
 
     switch (shared.execute()) {
       case TP_SUCCESS:
         *status = ExecutionParallel;
@@ -1359,19 +1276,19 @@ ForkJoinOperation::parallelExecution(Exe
 
 ForkJoinOperation::TrafficLight
 ForkJoinOperation::recoverFromBailout(ExecutionStatus *status)
 {
     // GreenLight: bailout recovered, try to compile-and-run again
     // RedLight: fatal error
 
     bailouts += 1;
+    determineBailoutCause();
 
-    if (!reportBailoutWarnings())
-        return fatalError(status);
+    SpewBailout(bailouts, bailoutScript, bailoutBytecode, bailoutCause);
 
     // After any bailout, we always scan over callee list of main
     // function, if nothing else
     RootedScript mainScript(cx_, fun_->nonLazyScript());
     if (!addToWorklist(mainScript))
         return fatalError(status);
 
     // Also invalidate and recompile any callees that were implicated
@@ -1425,34 +1342,35 @@ class ParallelIonInvoke
         JitCode *code = ion->method();
         jitcode_ = code->raw();
         enter_ = rt->jitRuntime()->enterIon();
         calleeToken_ = CalleeToToken(callee);
     }
 
     bool invoke(ForkJoinContext *cx) {
         JitActivation activation(cx);
-        Value result = Int32Value(argc_);
+        // In-out parameter: on input it denotes the number of values to preserve after the call.
+        Value result = Int32Value(0);
         CALL_GENERATED_CODE(enter_, jitcode_, argc_ + 1, argv_ + 1, nullptr, calleeToken_,
                             nullptr, 0, &result);
         return !result.isMagic();
     }
 };
 
 /////////////////////////////////////////////////////////////////////////////
 // ForkJoinShared
 //
 
 ForkJoinShared::ForkJoinShared(JSContext *cx,
                                ThreadPool *threadPool,
                                HandleFunction fun,
                                HandleObject updatable,
                                uint16_t sliceStart,
                                uint16_t sliceEnd,
-                               Vector<ParallelBailoutRecord> &records)
+                               ParallelBailoutRecord *records)
   : cx_(cx),
     threadPool_(threadPool),
     fun_(fun),
     updatable_(updatable),
     sliceStart_(sliceStart),
     sliceEnd_(sliceEnd),
     cxLock_(nullptr),
     records_(records),
@@ -1643,35 +1561,35 @@ ForkJoinShared::executePortion(PerThread
     Spew(SpewOps, "Up");
 
     // Make a new IonContext for the slice, which is needed if we need to
     // re-enter the VM.
     IonContext icx(CompileRuntime::get(cx_->runtime()),
                    CompileCompartment::get(cx_->compartment()),
                    nullptr);
 
-    JS_ASSERT(!cx.bailoutRecord->bailedOut());
+    JS_ASSERT(cx.bailoutRecord->topScript == nullptr);
 
     if (!fun_->nonLazyScript()->hasParallelIonScript()) {
         // Sometimes, particularly with GCZeal, the parallel ion
         // script can be collected between starting the parallel
         // op and reaching this point.  In that case, we just fail
         // and fallback.
         Spew(SpewOps, "Down (Script no longer present)");
-        cx.bailoutRecord->joinCause(ParallelBailoutMainScriptNotPresent);
+        cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent);
         setAbortFlagAndRequestInterrupt(false);
     } else {
         ParallelIonInvoke<3> fii(runtime(), fun_, 3);
 
         fii.args[0] = Int32Value(worker->id());
         fii.args[1] = Int32Value(sliceStart_);
         fii.args[2] = Int32Value(sliceEnd_);
 
         bool ok = fii.invoke(&cx);
-        JS_ASSERT(ok == !cx.bailoutRecord->bailedOut());
+        JS_ASSERT(ok == !cx.bailoutRecord->topScript);
         if (!ok) {
             setAbortFlagAndRequestInterrupt(false);
 #ifdef JSGC_FJGENERATIONAL
             // TODO: See bugs 1010169, 993347.
             //
             // It is not desirable to promote here, but if we don't do
             // this then we can't unconditionally transfer arenas to
             // the compartment, since the arenas can contain objects
@@ -1707,17 +1625,17 @@ ForkJoinShared::setAbortFlagDueToInterru
 {
     JS_ASSERT(cx_->runtime()->interruptPar);
     // The GC Needed flag should not be set during parallel
     // execution.  Instead, one of the requestGC() or
     // requestZoneGC() methods should be invoked.
     JS_ASSERT(!cx_->runtime()->gc.isNeeded);
 
     if (!abort_) {
-        cx.bailoutRecord->joinCause(ParallelBailoutInterrupt);
+        cx.bailoutRecord->setCause(ParallelBailoutInterrupt);
         setAbortFlagAndRequestInterrupt(false);
     }
 }
 
 void
 ForkJoinShared::setAbortFlagAndRequestInterrupt(bool fatal)
 {
     AutoLockMonitor lock(*this);
@@ -1881,108 +1799,99 @@ ForkJoinContext::check()
     }
     return true;
 }
 
 void
 ForkJoinContext::requestGC(JS::gcreason::Reason reason)
 {
     shared_->requestGC(reason);
-    bailoutRecord->joinCause(ParallelBailoutRequestedGC);
+    bailoutRecord->setCause(ParallelBailoutRequestedGC);
     shared_->setAbortFlagAndRequestInterrupt(false);
 }
 
 void
 ForkJoinContext::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
 {
     shared_->requestZoneGC(zone, reason);
-    bailoutRecord->joinCause(ParallelBailoutRequestedZoneGC);
+    bailoutRecord->setCause(ParallelBailoutRequestedZoneGC);
     shared_->setAbortFlagAndRequestInterrupt(false);
 }
 
 bool
 ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
 {
     shared_->setPendingAbortFatal();
-    bailoutRecord->joinCause(cause);
+    bailoutRecord->setCause(cause);
     return false;
 }
 
 //////////////////////////////////////////////////////////////////////////////
 // ParallelBailoutRecord
 
-ParallelBailoutRecord::~ParallelBailoutRecord()
+void
+js::ParallelBailoutRecord::init(JSContext *cx)
 {
-    reset();
-    js_delete(frames_);
+    reset(cx);
 }
 
-bool
-ParallelBailoutRecord::init(JSContext *cx)
+void
+js::ParallelBailoutRecord::reset(JSContext *cx)
 {
-    MOZ_ASSERT(!frames_);
-    frames_ = cx->new_<Vector<RematerializedFrame *> >(cx);
-    return !!frames_;
+    topScript = nullptr;
+    cause = ParallelBailoutNone;
+    depth = 0;
+}
+
+void
+js::ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
+                                    JSScript *outermostScript,
+                                    JSScript *currentScript,
+                                    jsbytecode *currentPc)
+{
+    this->cause = cause;
+    updateCause(cause, outermostScript, currentScript, currentPc);
 }
 
 void
-ParallelBailoutRecord::reset()
+js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
+                                       JSScript *outermostScript,
+                                       JSScript *currentScript,
+                                       jsbytecode *currentPc)
 {
-    RematerializedFrame::FreeInVector(frames());
-    cause = ParallelBailoutNone;
-}
-
-template <class T>
-static void
-RematerializeFramesWithIter(ForkJoinContext *cx, T &frameIter,
-                            Vector<RematerializedFrame *> &frames)
-{
-    // This function as well as |rematerializeFrames| methods below are
-    // infallible. These are only called when we are already erroring out. If
-    // we OOM here, free what we've allocated and return. Error reporting is
-    // then unable to give the user detailed stack information.
-
-    MOZ_ASSERT(frames.empty());
+    JS_ASSERT_IF(outermostScript, currentScript);
+    JS_ASSERT_IF(outermostScript, outermostScript->hasParallelIonScript());
+    JS_ASSERT_IF(currentScript, outermostScript);
+    JS_ASSERT_IF(!currentScript, !currentPc);
 
-    for (; !frameIter.done(); ++frameIter) {
-        if (!frameIter.isIonJS())
-            continue;
-
-        InlineFrameIterator inlineIter(cx, &frameIter);
-        Vector<RematerializedFrame *> inlineFrames(cx);
+    if (this->cause == ParallelBailoutNone)
+        this->cause = cause;
 
-        if (!RematerializedFrame::RematerializeInlineFrames(cx, frameIter.fp(),
-                                                            inlineIter, inlineFrames))
-        {
-            RematerializedFrame::FreeInVector(inlineFrames);
-            RematerializedFrame::FreeInVector(frames);
-            return;
-        }
+    if (outermostScript)
+        this->topScript = outermostScript;
 
-        // Reverse the inline frames into the main vector.
-        while (!inlineFrames.empty()) {
-            if (!frames.append(inlineFrames.popCopy())) {
-                RematerializedFrame::FreeInVector(inlineFrames);
-                RematerializedFrame::FreeInVector(frames);
-                return;
-            }
-        }
-    }
+    if (currentScript)
+        addTrace(currentScript, currentPc);
 }
 
 void
-ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, JitFrameIterator &frameIter)
+js::ParallelBailoutRecord::addTrace(JSScript *script,
+                                    jsbytecode *pc)
 {
-    RematerializeFramesWithIter(cx, frameIter, frames());
-}
+    // Ideally, this should never occur, because we should always have
+    // a script when we invoke setCause, but I havent' fully
+    // refactored things to that point yet:
+    if (topScript == nullptr && script != nullptr)
+        topScript = script;
 
-void
-ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, IonBailoutIterator &frameIter)
-{
-    RematerializeFramesWithIter(cx, frameIter, frames());
+    if (depth < MaxDepth) {
+        trace[depth].script = script;
+        trace[depth].bytecode = pc;
+        depth += 1;
+    }
 }
 
 //////////////////////////////////////////////////////////////////////////////
 
 //
 // Debug spew
 //
 
@@ -2246,16 +2155,31 @@ class ParallelSpewer
 
         char buf[BufferSize];
         JS_vsnprintf(buf, BufferSize, fmt, ap);
 
         JSScript *script = mir->block()->info().script();
         spew(SpewCompile, "%s%s%s: %s (%s:%u)", cyan(), mir->opName(), reset(), buf,
              script->filename(), PCToLineNumber(script, mir->trackedPc()));
     }
+
+    void spewBailoutIR(IonLIRTraceData *data) {
+        if (!active[SpewBailouts])
+            return;
+
+        // If we didn't bail from a LIR/MIR but from a propagated parallel
+        // bailout, don't bother printing anything since we've printed it
+        // elsewhere.
+        if (data->mirOpName && data->script) {
+            spew(SpewBailouts, "%sBailout%s: %s / %s%s%s (block %d lir %d) (%s:%u)", yellow(), reset(),
+                 data->lirOpName, cyan(), data->mirOpName, reset(),
+                 data->blockIndex, data->lirIndex, data->script->filename(),
+                 PCToLineNumber(data->script, data->pc));
+        }
+    }
 };
 
 // Singleton instance of the spewer.
 static ParallelSpewer spewer;
 
 bool
 parallel::SpewEnabled(SpewChannel channel)
 {
@@ -2314,16 +2238,22 @@ void
 parallel::SpewMIR(MDefinition *mir, const char *fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
     spewer.spewMIR(mir, fmt, ap);
     va_end(ap);
 }
 
+void
+parallel::SpewBailoutIR(IonLIRTraceData *data)
+{
+    spewer.spewBailoutIR(data);
+}
+
 #endif // FORKJOIN_SPEW
 
 bool
 js::InExclusiveParallelSection()
 {
     return InParallelSection() && ForkJoinContext::current()->hasAcquiredJSContext();
 }
 
--- a/js/src/vm/ForkJoin.h
+++ b/js/src/vm/ForkJoin.h
@@ -267,116 +267,103 @@ class ForkJoinActivation : public Activa
     explicit ForkJoinActivation(JSContext *cx);
     ~ForkJoinActivation();
 };
 
 class ForkJoinContext;
 
 bool ForkJoin(JSContext *cx, CallArgs &args);
 
+struct IonLIRTraceData {
+    uint32_t blockIndex;
+    uint32_t lirIndex;
+    uint32_t execModeInt;
+    const char *lirOpName;
+    const char *mirOpName;
+    JSScript *script;
+    jsbytecode *pc;
+};
+
 ///////////////////////////////////////////////////////////////////////////
 // Bailout tracking
 
-//
-// The lattice of causes goes:
-//
-//       { everything else }
-//               |
-//           Interrupt
-//           /       \
-//   Unsupported   UnsupportedVM
-//           \       /
-//              None
-//
 enum ParallelBailoutCause {
-    ParallelBailoutNone = 0,
-
-    ParallelBailoutUnsupported,
-    ParallelBailoutUnsupportedVM,
-
-    // The periodic interrupt failed, which can mean that either
-    // another thread canceled, the user interrupted us, etc
-    ParallelBailoutInterrupt,
+    ParallelBailoutNone,
 
     // Compiler returned Method_Skipped
     ParallelBailoutCompilationSkipped,
 
     // Compiler returned Method_CantCompile
     ParallelBailoutCompilationFailure,
 
-    // Propagating a failure, i.e., another thread requested the computation
-    // be aborted.
-    ParallelBailoutPropagate,
+    // The periodic interrupt failed, which can mean that either
+    // another thread canceled, the user interrupted us, etc
+    ParallelBailoutInterrupt,
 
     // An IC update failed
     ParallelBailoutFailedIC,
 
     // Heap busy flag was set during interrupt
     ParallelBailoutHeapBusy,
 
     ParallelBailoutMainScriptNotPresent,
     ParallelBailoutCalledToUncompiledScript,
     ParallelBailoutIllegalWrite,
     ParallelBailoutAccessToIntrinsic,
     ParallelBailoutOverRecursed,
     ParallelBailoutOutOfMemory,
+    ParallelBailoutUnsupported,
+    ParallelBailoutUnsupportedVM,
     ParallelBailoutUnsupportedStringComparison,
     ParallelBailoutRequestedGC,
-    ParallelBailoutRequestedZoneGC
+    ParallelBailoutRequestedZoneGC,
 };
 
-namespace jit {
-class BailoutStack;
-class JitFrameIterator;
-class RematerializedFrame;
-}
+struct ParallelBailoutTrace {
+    JSScript *script;
+    jsbytecode *bytecode;
+};
 
 // See "Bailouts" section in comment above.
-struct ParallelBailoutRecord
-{
-    // Captured Ion frames at the point of bailout. Stored younger-to-older,
-    // i.e., the 0th frame is the youngest frame.
-    Vector<jit::RematerializedFrame *> *frames_;
+struct ParallelBailoutRecord {
+    JSScript *topScript;
     ParallelBailoutCause cause;
 
-    ParallelBailoutRecord()
-      : frames_(nullptr),
-        cause(ParallelBailoutNone)
-    { }
-
-    ~ParallelBailoutRecord();
-
-    bool init(JSContext *cx);
-    void reset();
+    // Eventually we will support deeper traces,
+    // but for now we gather at most a single frame.
+    static const uint32_t MaxDepth = 1;
+    uint32_t depth;
+    ParallelBailoutTrace trace[MaxDepth];
 
-    Vector<jit::RematerializedFrame *> &frames() { MOZ_ASSERT(frames_); return *frames_; }
-    bool hasFrames() const { return frames_ && !frames_->empty(); }
-    bool bailedOut() const { return cause != ParallelBailoutNone; }
-
-    void joinCause(ParallelBailoutCause cause) {
-        if (this->cause <= ParallelBailoutInterrupt &&
-            (cause > ParallelBailoutInterrupt || cause > this->cause))
-        {
-            this->cause = cause;
-        }
-    }
-
-    void rematerializeFrames(ForkJoinContext *cx, jit::JitFrameIterator &frameIter);
-    void rematerializeFrames(ForkJoinContext *cx, jit::IonBailoutIterator &frameIter);
+    void init(JSContext *cx);
+    void reset(JSContext *cx);
+    void setCause(ParallelBailoutCause cause,
+                  JSScript *outermostScript = nullptr,   // inliner (if applicable)
+                  JSScript *currentScript = nullptr,     // inlinee (if applicable)
+                  jsbytecode *currentPc = nullptr);
+    void updateCause(ParallelBailoutCause cause,
+                     JSScript *outermostScript,
+                     JSScript *currentScript,
+                     jsbytecode *currentPc);
+    void addTrace(JSScript *script,
+                  jsbytecode *pc);
 };
 
 class ForkJoinShared;
 
 class ForkJoinContext : public ThreadSafeContext
 {
   public:
     // Bailout record used to record the reason this thread stopped executing
     ParallelBailoutRecord *const bailoutRecord;
 
 #ifdef FORKJOIN_SPEW
+    // Records the last instr. to execute on this thread.
+    IonLIRTraceData traceData;
+
     // The maximum worker id.
     uint32_t maxWorkerId;
 #endif
 
     // When we run a par operation like mapPar, we create an out pointer
     // into a specific region of the destination buffer. Even though the
     // destination buffer is not thread-local, it is permissible to write into
     // it via the handles provided. These two fields identify the memory
@@ -587,31 +574,33 @@ void Spew(SpewChannel channel, const cha
 void SpewVA(SpewChannel channel, const char *fmt, va_list args);
 void SpewBeginOp(JSContext *cx, const char *name);
 void SpewBailout(uint32_t count, HandleScript script, jsbytecode *pc,
                  ParallelBailoutCause cause);
 ExecutionStatus SpewEndOp(ExecutionStatus status);
 void SpewBeginCompile(HandleScript script);
 jit::MethodStatus SpewEndCompile(jit::MethodStatus status);
 void SpewMIR(jit::MDefinition *mir, const char *fmt, ...);
+void SpewBailoutIR(IonLIRTraceData *data);
 
 #else
 
 static inline bool SpewEnabled(SpewChannel channel) { return false; }
 static inline void Spew(SpewChannel channel, const char *fmt, ...) { }
 static inline void SpewVA(SpewChannel channel, const char *fmt, va_list args) { }
 static inline void SpewBeginOp(JSContext *cx, const char *name) { }
 static inline void SpewBailout(uint32_t count, HandleScript script,
                                jsbytecode *pc, ParallelBailoutCause cause) {}
 static inline ExecutionStatus SpewEndOp(ExecutionStatus status) { return status; }
 static inline void SpewBeginCompile(HandleScript script) { }
 #ifdef JS_ION
 static inline jit::MethodStatus SpewEndCompile(jit::MethodStatus status) { return status; }
 static inline void SpewMIR(jit::MDefinition *mir, const char *fmt, ...) { }
 #endif
+static inline void SpewBailoutIR(IonLIRTraceData *data) { }
 
 #endif // FORKJOIN_SPEW && JS_THREADSAFE && JS_ION
 
 } // namespace parallel
 } // namespace js
 
 /* static */ inline js::ForkJoinContext *
 js::ForkJoinContext::current()
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1582,102 +1582,119 @@ jit::JitActivation::setActive(JSContext 
         cx->mainThread().jitTop = prevJitTop_;
         cx->mainThread().jitJSContext = prevJitJSContext_;
     }
 }
 
 #ifdef JS_ION
 
 void
+jit::JitActivation::freeRematerializedFramesInVector(RematerializedFrameVector &frames)
+{
+    for (size_t i = 0; i < frames.length(); i++) {
+        RematerializedFrame *f = frames[i];
+        f->RematerializedFrame::~RematerializedFrame();
+        js_free(f);
+    }
+    frames.clear();
+}
+
+void
 jit::JitActivation::removeRematerializedFrame(uint8_t *top)
 {
     if (!rematerializedFrames_)
         return;
 
     if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
-        RematerializedFrame::FreeInVector(p->value());
+        freeRematerializedFramesInVector(p->value());
         rematerializedFrames_->remove(p);
     }
 }
 
 void
 jit::JitActivation::clearRematerializedFrames()
 {
     if (!rematerializedFrames_)
         return;
 
     for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront()) {
-        RematerializedFrame::FreeInVector(e.front().value());
+        freeRematerializedFramesInVector(e.front().value());
         e.removeFront();
     }
 }
 
-template <class T>
 jit::RematerializedFrame *
-jit::JitActivation::getRematerializedFrame(ThreadSafeContext *cx, const T &iter, size_t inlineDepth)
+jit::JitActivation::getRematerializedFrame(ThreadSafeContext *cx, JitFrameIterator &iter,
+                                           size_t inlineDepth)
 {
     // Only allow rematerializing from the same thread.
     MOZ_ASSERT(cx->perThreadData == cx_->perThreadData);
     MOZ_ASSERT(iter.activation() == this);
     MOZ_ASSERT(iter.isIonJS());
 
     if (!rematerializedFrames_) {
         rematerializedFrames_ = cx->new_<RematerializedFrameTable>(cx);
         if (!rematerializedFrames_ || !rematerializedFrames_->init()) {
             rematerializedFrames_ = nullptr;
             return nullptr;
         }
     }
 
+    // The unit of rematerialization is an uninlined frame and its inlined
+    // frames. Since inlined frames do not exist outside of snapshots, it is
+    // impossible to synchronize their rematerialized copies to preserve
+    // identity. Therefore, we always rematerialize an uninlined frame and all
+    // its inlined frames at once.
+
     uint8_t *top = iter.fp();
     RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top);
     if (!p) {
         RematerializedFrameVector empty(cx);
         if (!rematerializedFrames_->add(p, top, Move(empty)))
             return nullptr;
 
-        // The unit of rematerialization is an uninlined frame and its inlined
-        // frames. Since inlined frames do not exist outside of snapshots, it
-        // is impossible to synchronize their rematerialized copies to
-        // preserve identity. Therefore, we always rematerialize an uninlined
-        // frame and all its inlined frames at once.
         InlineFrameIterator inlineIter(cx, &iter);
-        if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, p->value()))
+        if (!p->value().resize(inlineIter.frameCount()))
             return nullptr;
+
+        while (true) {
+            size_t frameNo = inlineIter.frameNo();
+            p->value()[frameNo] = RematerializedFrame::New(cx, top, inlineIter);
+            if (!p->value()[frameNo])
+                return nullptr;
+
+            if (!inlineIter.more())
+                break;
+            ++inlineIter;
+        }
     }
 
     return p->value()[inlineDepth];
 }
 
-template jit::RematerializedFrame *
-jit::JitActivation::getRematerializedFrame<jit::JitFrameIterator>(ThreadSafeContext *cx,
-                                                                  const jit::JitFrameIterator &iter,
-                                                                  size_t inlineDepth);
-template jit::RematerializedFrame *
-jit::JitActivation::getRematerializedFrame<jit::IonBailoutIterator>(ThreadSafeContext *cx,
-                                                                    const jit::IonBailoutIterator &iter,
-                                                                    size_t inlineDepth);
-
 jit::RematerializedFrame *
 jit::JitActivation::lookupRematerializedFrame(uint8_t *top, size_t inlineDepth)
 {
     if (!rematerializedFrames_)
         return nullptr;
     if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top))
         return inlineDepth < p->value().length() ? p->value()[inlineDepth] : nullptr;
     return nullptr;
 }
 
 void
 jit::JitActivation::markRematerializedFrames(JSTracer *trc)
 {
     if (!rematerializedFrames_)
         return;
-    for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront())
-        RematerializedFrame::MarkInVector(trc, e.front().value());
+    for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront()) {
+        RematerializedFrameVector &frames = e.front().value();
+        for (size_t i = 0; i < frames.length(); i++)
+            frames[i]->mark(trc);
+    }
 }
 
 #endif // JS_ION
 
 AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
   : Activation(cx, AsmJS),
     module_(module),
     errorRejoinSP_(nullptr),
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1333,20 +1333,21 @@ class JitActivation : public Activation
     bool active_;
 
 #ifdef JS_ION
     // Rematerialized Ion frames which has info copied out of snapshots. Maps
     // frame pointers (i.e. jitTop) to a vector of rematerializations of all
     // inline frames associated with that frame.
     //
     // This table is lazily initialized by calling getRematerializedFrame.
-    typedef Vector<RematerializedFrame *> RematerializedFrameVector;
+    typedef Vector<RematerializedFrame *, 1> RematerializedFrameVector;
     typedef HashMap<uint8_t *, RematerializedFrameVector> RematerializedFrameTable;
     RematerializedFrameTable *rematerializedFrames_;
 
+    void freeRematerializedFramesInVector(RematerializedFrameVector &frames);
     void clearRematerializedFrames();
 #endif
 
 #ifdef CHECK_OSIPOINT_REGISTERS
   protected:
     // Used to verify that live registers don't change between a VM call and
     // the OsiPoint that follows it. Protected to silence Clang warning.
     uint32_t checkRegs_;
@@ -1393,21 +1394,18 @@ class JitActivation : public Activation
 #endif
 
 #ifdef JS_ION
     // Look up a rematerialized frame keyed by the fp, rematerializing the
     // frame if one doesn't already exist. A frame can only be rematerialized
     // if an IonFrameIterator pointing to the nearest uninlined frame can be
     // provided, as values need to be read out of snapshots.
     //
-    // T is either JitFrameIterator or IonBailoutIterator.
-    //
     // The inlineDepth must be within bounds of the frame pointed to by iter.
-    template <class T>
-    RematerializedFrame *getRematerializedFrame(ThreadSafeContext *cx, const T &iter,
+    RematerializedFrame *getRematerializedFrame(ThreadSafeContext *cx, JitFrameIterator &iter,
                                                 size_t inlineDepth = 0);
 
     // Look up a rematerialized frame by the fp. If inlineDepth is out of
     // bounds of what has been rematerialized, nullptr is returned.
     RematerializedFrame *lookupRematerializedFrame(uint8_t *top, size_t inlineDepth = 0);
 
     bool hasRematerializedFrame(uint8_t *top, size_t inlineDepth = 0) {
         return !!lookupRematerializedFrame(top, inlineDepth);