Bug 1019304 - Part 2: Overhaul PJS bailout mechanism to be like the normal bailout mechanism. (r=nmatsakis)
authorShu-yu Guo <shu@rfrn.org>
Fri, 20 Jun 2014 18:39:14 -0700
changeset 210711 cd7125c3338572cbb59534ed03243a8a87fbc92c
parent 210710 feaac6c10dc6dd76e908bdc9185cc8849bbb8595
child 210712 c6bd4f36fc3026dc9fed67ec54f23492b9fe9618
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnmatsakis
bugs1019304
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1019304 - Part 2: Overhaul PJS bailout mechanism to be like the normal bailout mechanism. (r=nmatsakis) - Remove ad-hoc IR tracing and AbortPar machinery. - PJS bailouts use a different handler that unwinds the entire ForkJoin stack, but otherwise shares the same code as sequential bailouts. - Each thread's stack is snapshotted as a RematerializedFrame vector.
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/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
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -64,20 +64,16 @@ 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,16 +95,20 @@ 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,17 +1224,20 @@ class OutOfLineInterruptCheckImplicit : 
     { }
 
     bool accept(CodeGenerator *codegen) {
         return codegen->visitOutOfLineInterruptCheckImplicit(this);
     }
 };
 
 typedef bool (*InterruptCheckFn)(JSContext *);
-static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
+typedef bool (*InterruptCheckParFn)(ForkJoinContext *);
+static const VMFunctionsModal InterruptCheckInfo = VMFunctionsModal(
+    FunctionInfo<InterruptCheckFn>(InterruptCheck),
+    FunctionInfo<InterruptCheckParFn>(InterruptCheckPar));
 
 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
@@ -1844,23 +1847,17 @@ 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));
 
-    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;
+    return bailoutIfFalseBool(ReturnReg, lir->snapshot());
 }
 
 bool
 CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity *guard)
 {
     Register obj = ToRegister(guard->input());
 
     Assembler::Condition cond =
@@ -2378,37 +2375,33 @@ 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)
 {
-    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());
+    pushArg(calleeReg);
+    if (!callVM(CallToUncompiledScriptParInfo, lir))
+        return false;
+    masm.assumeUnreachable("CallToUncompiledScriptParInfo always returns false.");
     return true;
 }
 
 bool
 CodeGenerator::visitCallKnown(LCallKnown *call)
 {
     Register calleereg = ToRegister(call->getFunction());
     Register objreg    = ToRegister(call->getTempObject());
@@ -2473,49 +2466,30 @@ 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);
@@ -2917,28 +2891,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>
 {
-    LCheckOverRecursed *lir_;
+    LInstruction *lir_;
 
   public:
-    explicit CheckOverRecursedFailure(LCheckOverRecursed *lir)
+    explicit CheckOverRecursedFailure(LInstruction *lir)
       : lir_(lir)
     { }
 
     bool accept(CodeGenerator *codegen) {
         return codegen->visitCheckOverRecursedFailure(this);
     }
 
-    LCheckOverRecursed *lir() const {
+    LInstruction *lir() const {
         return lir_;
     }
 };
 
 bool
 CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed *lir)
 {
     // If we don't push anything on the stack, skip the check.
@@ -2998,19 +2972,21 @@ CodeGenerator::visitDefFun(LDefFun *lir)
 
     pushArg(ImmGCPtr(lir->mir()->fun()));
     pushArg(scopeChain);
     pushArg(ImmGCPtr(current->mir()->info().script()));
 
     return callVM(DefFunOperationInfo, lir);
 }
 
-typedef bool (*ReportOverRecursedFn)(JSContext *);
-static const VMFunction CheckOverRecursedInfo =
-    FunctionInfo<ReportOverRecursedFn>(CheckOverRecursed);
+typedef bool (*CheckOverRecursedFn)(JSContext *);
+typedef bool (*CheckOverRecursedParFn)(ForkJoinContext *);
+static const VMFunctionsModal CheckOverRecursedInfo = VMFunctionsModal(
+    FunctionInfo<CheckOverRecursedFn>(CheckOverRecursed),
+    FunctionInfo<CheckOverRecursedParFn>(CheckOverRecursedPar));
 
 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
@@ -3021,35 +2997,16 @@ 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
@@ -3057,111 +3014,41 @@ 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.
-    CheckOverRecursedFailurePar *ool = new(alloc()) CheckOverRecursedFailurePar(lir);
+    CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(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_.
-    OutOfLineInterruptCheckPar *ool = new(alloc()) OutOfLineInterruptCheckPar(lir);
-    if (!addOutOfLineCode(ool))
+    OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
+    if (!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;
@@ -3454,19 +3341,16 @@ 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
         }
@@ -3905,22 +3789,16 @@ 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)
@@ -4018,35 +3896,20 @@ 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);
 
-    OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, ool->lir);
-    if (!bail)
-        return false;
-    masm.branchTestPtr(Assembler::Zero, out, out, bail->entry());
-    masm.jump(ool->rejoin());
-    return true;
+    return bailoutTestPtr(Assembler::Zero, out, out, ool->lir->snapshot());
 }
 #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)
 {
@@ -8548,39 +8411,16 @@ 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).
@@ -8614,32 +8454,16 @@ 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,17 +147,16 @@ 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);
@@ -296,31 +295,28 @@ 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);
@@ -369,17 +365,16 @@ 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,20 +221,27 @@ 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);
+        bailoutHandler_ = generateBailoutHandler(cx, SequentialExecution);
         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,38 +750,28 @@ 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");
 
-    while (!iter.isEntry()) {
-        if (iter.isScripted()) {
-            cx->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM,
-                                           iter.script(), iter.script(), nullptr);
-            break;
-        }
-        ++iter;
-    }
+    ForkJoinContext *cx = ForkJoinContext::current();
+    JitFrameIterator frameIter(cx);
 
-    while (!iter.isEntry()) {
-        if (iter.isScripted())
-            PropagateAbortPar(iter.script(), iter.script());
-        ++iter;
-    }
+    cx->bailoutRecord->joinCause(ParallelBailoutUnsupportedVM);
+    cx->bailoutRecord->rematerializeFrames(cx, frameIter);
 
     rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
-    rfe->stackPointer = iter.fp();
+
+    MOZ_ASSERT(frameIter.done());
+    rfe->stackPointer = frameIter.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,17 +244,16 @@ 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"
@@ -297,18 +296,16 @@ 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,18 +49,16 @@ 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,16 +166,19 @@ 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_;
 
@@ -232,17 +235,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);
+    JitCode *generateBailoutHandler(JSContext *cx, ExecutionMode mode);
     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);
@@ -299,18 +302,22 @@ 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() const {
-        return bailoutHandler_;
+    JitCode *getGenericBailoutHandler(ExecutionMode mode) const {
+        switch (mode) {
+          case SequentialExecution: return bailoutHandler_;
+          case ParallelExecution:   return parallelBailoutHandler_;
+          default:                  MOZ_ASSUME_UNREACHABLE("No such execution mode");
+        }
     }
 
     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,22 +566,16 @@ 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,17 +29,16 @@
     _(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,23 +113,17 @@ LIRGenerator::visitTableSwitch(MTableSwi
     }
     return add(newLTableSwitch(index, tempInt, tableswitch));
 }
 
 bool
 LIRGenerator::visitCheckOverRecursed(MCheckOverRecursed *ins)
 {
     LCheckOverRecursed *lir = new(alloc()) LCheckOverRecursed();
-
-    if (!add(lir, ins))
-        return false;
-    if (!assignSafepoint(lir, ins))
-        return false;
-
-    return true;
+    return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitCheckOverRecursedPar(MCheckOverRecursedPar *ins)
 {
     LCheckOverRecursedPar *lir =
         new(alloc()) LCheckOverRecursedPar(useRegister(ins->forkJoinContext()), temp());
     return add(lir, ins) && assignSafepoint(lir, ins);
@@ -233,23 +227,16 @@ 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;
 
@@ -2174,17 +2161,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 add(lir, ins);
+    return assignSnapshot(lir, Bailout_GuardThreadExclusive) && 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,17 +72,16 @@ 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,34 +1816,16 @@ 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)
     {
@@ -2287,20 +2269,21 @@ class MApplyArgs
     TypePolicy *typePolicy() {
         return this;
     }
     bool possiblyCalls() const {
         return true;
     }
 };
 
-class MBail : public MNullaryInstruction
+class MBail : public MAryControlInstruction<0, 0>
 {
   protected:
     MBail(BailoutKind kind)
+      : MAryControlInstruction<0, 0>()
     {
         bailoutKind_ = kind;
         setGuard();
     }
 
   private:
     BailoutKind bailoutKind_;
 
@@ -5248,29 +5231,31 @@ 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,17 +743,25 @@ MBasicBlock::discardDefAt(MDefinitionIte
         iter.iter_ = iter.block_->discardAt(iter.iter_);
 
     return iter;
 }
 
 void
 MBasicBlock::discardAllInstructions()
 {
-    for (MInstructionIterator iter = begin(); iter != end(); ) {
+    MInstructionIterator iter = begin();
+    discardAllInstructionsStartingAt(iter);
+
+}
+
+void
+MBasicBlock::discardAllInstructionsStartingAt(MInstructionIterator &iter)
+{
+    while (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,16 +227,17 @@ 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,17 +212,16 @@ 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,73 +120,16 @@ 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.
@@ -194,58 +137,56 @@ 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->setCause(ParallelBailoutOverRecursed);
+        cx->bailoutRecord->joinCause(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->setCause(ParallelBailoutOverRecursed);
+        cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
         return false;
     }
 
     return InterruptCheckPar(cx);
 }
 
 bool
 jit::InterruptCheckPar(ForkJoinContext *cx)
 {
     JS_ASSERT(ForkJoinContext::current() == cx);
     bool result = cx->check();
     if (!result) {
-        // 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.
+        cx->bailoutRecord->joinCause(ParallelBailoutInterrupt);
         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)
+    if (res != JSObject::ED_OK) {
+        fprintf(stderr, "==== NGNG\n");
         return nullptr;
+    }
     return array;
 }
 
 bool
 jit::SetPropertyPar(ForkJoinContext *cx, HandleObject obj, HandlePropertyName name,
                     HandleValue value, bool strict, jsbytecode *pc)
 {
     JS_ASSERT(cx->isThreadLocal(obj));
@@ -568,68 +509,46 @@ 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::AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
-              jsbytecode *bytecode)
+jit::BailoutPar(BailoutStack *sp, uint8_t **entryFramePointer)
 {
-    // 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());
+    parallel::Spew(parallel::SpewBailouts, "Bailing");
 
     ForkJoinContext *cx = ForkJoinContext::current();
 
-    JS_ASSERT(cx->bailoutRecord->depth == 0);
-    cx->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode);
+    // 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();
 }
 
-void
-jit::PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript)
+bool
+jit::CallToUncompiledScriptPar(ForkJoinContext *cx, JSObject *obj)
 {
-    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;
+        return false;
     }
 
     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()) {
@@ -651,16 +570,18 @@ jit::CallToUncompiledScriptPar(JSObject 
         } 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,20 +67,15 @@ 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 AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
-              jsbytecode *bytecode);
-void PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript);
-
-void TraceLIR(IonLIRTraceData *current);
-
-void CallToUncompiledScriptPar(JSObject *obj);
+void BailoutPar(BailoutStack *sp, uint8_t **entryFramePointer);
+bool CallToUncompiledScriptPar(ForkJoinContext *cx, JSObject *obj);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_ParallelFunctions_h */
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -70,16 +70,17 @@ 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_);
@@ -101,17 +102,17 @@ class ParallelSafetyVisitor : public MIn
     void clearUnsafe() { unsafe_ = false; }
     bool unsafe() { return unsafe_; }
     MDefinition *ForkJoinContext() {
         if (!cx_)
             cx_ = graph_.forkJoinContext();
         return cx_;
     }
 
-    bool convertToBailout(MBasicBlock *block, MInstruction *ins);
+    bool convertToBailout(MInstructionIterator &iter);
 
     // 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)
@@ -276,17 +277,16 @@ 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,16 +324,26 @@ 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
@@ -349,31 +359,30 @@ 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 *instr = nullptr;
-            for (MInstructionIterator ins(block->begin());
-                 ins != block->end() && !visitor.unsafe();)
-            {
+            MInstruction *ins = nullptr;
+            MInstructionIterator iter(block->begin());
+            while (iter != block->end() && !visitor.unsafe()) {
                 if (mir_->shouldCancel("ParallelSafetyAnalysis"))
                     return false;
 
                 // We may be removing or replacing the current
-                // instruction, so advance `ins` now.  Remember the
+                // instruction, so advance `iter` now.  Remember the
                 // last instr. we looked at for use later if it should
                 // prove unsafe.
-                instr = *ins++;
+                ins = *iter++;
 
-                if (!instr->accept(&visitor)) {
-                    SpewMIR(instr, "Unaccepted");
+                if (!ins->accept(&visitor)) {
+                    SpewMIR(ins, "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();
@@ -385,105 +394,59 @@ 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.
-                if (!visitor.convertToBailout(*block, instr))
+                // 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))
                     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
-ParallelSafetyAnalysis::removeResumePointOperands()
+ParallelSafetyVisitor::convertToBailout(MInstructionIterator &iter)
 {
-    // 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)
-{
+    // We expect iter to be settled on the unsafe instruction.
+    MInstruction *ins = *iter;
+    MBasicBlock *block = ins->block();
     JS_ASSERT(unsafe()); // `block` must have contained unsafe items
     JS_ASSERT(block->isMarked()); // `block` must have been reachable to get here
 
-    // 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++)
+    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++)
         block->getSuccessor(i)->removePredecessor(block);
-    clearUnsafe();
-    block->discardAllPhis();
-    block->discardAllInstructions();
-    block->end(MAbortPar::New(graph_.alloc()));
+    block->discardAllInstructionsStartingAt(iter);
+
+    // End the block in the bail.
+    MBail *bailout = MBail::New(graph_.alloc());
+    TransplantResumePoint(ins, bailout);
+    block->end(bailout);
     return true;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Memory allocation
 //
 // Simple memory allocation opcodes---those which ultimately compile
 // down to a (possibly inlined) invocation of NewGCThing()---are
@@ -498,42 +461,41 @@ ParallelSafetyVisitor::visitCreateThisWi
 
 bool
 ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins)
 {
     if (ins->templateObject()->hasDynamicSlots()) {
         SpewMIR(ins, "call with dynamic slots");
         return markUnsafe();
     }
-    replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
-    return true;
+
+    return replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
 }
 
 bool
 ParallelSafetyVisitor::visitNewRunOnceCallObject(MNewRunOnceCallObject *ins)
 {
     if (ins->templateObject()->hasDynamicSlots()) {
         SpewMIR(ins, "call with dynamic slots");
         return markUnsafe();
     }
-    replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
-    return true;
+
+    return replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
 }
 
 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
-    replace(ins, MLambdaPar::New(alloc(), ForkJoinContext(), ins));
-    return true;
+    return replace(ins, MLambdaPar::New(alloc(), ForkJoinContext(), ins));
 }
 
 bool
 ParallelSafetyVisitor::visitNewObject(MNewObject *newInstruction)
 {
     if (newInstruction->shouldUseVM()) {
         SpewMIR(newInstruction, "should use VM");
         return markUnsafe();
@@ -593,32 +555,28 @@ ParallelSafetyVisitor::visitToString(MTo
         return markUnsafe();
     return true;
 }
 
 bool
 ParallelSafetyVisitor::replaceWithNewPar(MInstruction *newInstruction,
                                          JSObject *templateObject)
 {
-    replace(newInstruction, MNewPar::New(alloc(), ForkJoinContext(), templateObject));
-    return true;
+    return replace(newInstruction, MNewPar::New(alloc(), ForkJoinContext(), templateObject));
 }
 
 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() &&
@@ -626,16 +584,27 @@ 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
@@ -789,23 +758,18 @@ ParallelSafetyVisitor::visitSpecializedI
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Throw
 
 bool
 ParallelSafetyVisitor::visitThrow(MThrow *thr)
 {
-    MBasicBlock *block = thr->block();
-    JS_ASSERT(block->lastIns() == thr);
-    block->discardLastIns();
-    MAbortPar *bailout = MAbortPar::New(alloc());
-    if (!bailout)
-        return false;
-    block->end(bailout);
+    JS_ASSERT(thr->block()->lastIns() == thr);
+    replaceLastIns(thr, MBail::New(alloc()));
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Callee extraction
 //
 // See comments in header file.
 
--- a/js/src/jit/ParallelSafetyAnalysis.h
+++ b/js/src/jit/ParallelSafetyAnalysis.h
@@ -15,25 +15,22 @@ 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 MAbortPar blocks.
+// graph and replaces them with MBail 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/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -173,42 +173,26 @@ 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();
+        JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler(gen->info().executionMode());
         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());
@@ -233,33 +217,16 @@ 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,16 +63,20 @@ 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,16 +11,17 @@
 #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 =
@@ -509,17 +510,17 @@ JitRuntime::generateArgumentsRectifier(J
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
 #endif
 
     return code;
 }
 
 static void
-GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
+PushBailoutFrame(MacroAssembler &masm, uint32_t frameClass, Register spArg)
 {
     // the stack should look like:
     // [IonFrame]
     // bailoutFrame.registersnapshot
     // bailoutFrame.fpsnapshot
     // bailoutFrame.snapshotOffset
     // bailoutFrame.frameSize
 
@@ -554,20 +555,27 @@ GenerateBailoutThunk(JSContext *cx, Macr
     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));
@@ -605,16 +613,42 @@ 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);
@@ -629,20 +663,30 @@ JitRuntime::generateBailoutTable(JSConte
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutTable");
 #endif
 
     return code;
 }
 
 JitCode *
-JitRuntime::generateBailoutHandler(JSContext *cx)
+JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
 {
     MacroAssembler masm(cx);
-    GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
+
+    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");
+    }
 
     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,32 +208,16 @@ 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,16 +88,21 @@ 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,20 +637,30 @@ JitRuntime::generateBailoutTable(JSConte
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutTable");
 #endif
 
     return code;
 }
 
 JitCode *
-JitRuntime::generateBailoutHandler(JSContext *cx)
+JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
 {
     MacroAssembler masm(cx);
-    GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
+
+    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");
+    }
 
     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,16 +335,23 @@ 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;
@@ -817,133 +824,16 @@ 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,18 +21,16 @@
 
 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
 {
@@ -463,38 +461,16 @@ 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);
@@ -765,59 +741,12 @@ 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();
+        JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler(gen->info().executionMode());
         masm.jmp(ImmPtr(handler->raw()), Relocation::JITCODE);
     }
 
     return true;
 }
 
 class BailoutJump {
     Assembler::Condition cond_;
@@ -393,32 +393,16 @@ 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,16 +70,20 @@ 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,16 +6,17 @@
 
 #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.
@@ -441,23 +442,29 @@ JitRuntime::generateArgumentsRectifier(J
     CodeOffsetLabel returnLabel(returnOffset);
     returnLabel.fixup(&masm);
     if (returnAddrOut)
         *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
     return code;
 }
 
 static void
-GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
+PushBailoutFrame(MacroAssembler &masm, Register spArg)
 {
     // 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, r8);
+    masm.movq(rsp, spArg);
+}
+
+static void
+GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
+{
+    PushBailoutFrame(masm, 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);
@@ -469,38 +476,72 @@ 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)
+JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
 {
     MacroAssembler masm;
 
-    GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
+    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");
+    }
 
     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,16 +11,17 @@
 #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;
@@ -450,26 +451,32 @@ JitRuntime::generateArgumentsRectifier(J
     CodeOffsetLabel returnLabel(returnOffset);
     returnLabel.fixup(&masm);
     if (returnAddrOut)
         *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
     return code;
 }
 
 static void
-GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
+PushBailoutFrame(MacroAssembler &masm, uint32_t frameClass, Register spArg)
 {
     // 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, eax);
+    masm.movl(esp, spArg);
+}
+
+static void
+GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
+{
+    PushBailoutFrame(masm, frameClass, 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);
@@ -503,16 +510,41 @@ 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);
@@ -526,21 +558,30 @@ JitRuntime::generateBailoutTable(JSConte
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
 #endif
 
     return code;
 }
 
 JitCode *
-JitRuntime::generateBailoutHandler(JSContext *cx)
+JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
 {
     MacroAssembler masm;
 
-    GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
+    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");
+    }
 
     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,16 +22,17 @@
 
 #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
 
@@ -112,36 +113,23 @@ ForkJoinContext::requestZoneGC(JS::Zone 
 bool
 ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
 {
     MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
     return false;
 }
 
 void
-ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
-                                JSScript *outermostScript,
-                                JSScript *currentScript,
-                                jsbytecode *currentPc)
+ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, JitFrameIterator &frameIter)
 {
     MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
 }
 
 void
-js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
-                                       JSScript *outermostScript,
-                                       JSScript *currentScript,
-                                       jsbytecode *currentPc)
-{
-    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
-}
-
-void
-ParallelBailoutRecord::addTrace(JSScript *script,
-                                jsbytecode *pc)
+ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, IonBailoutIterator &frameIter)
 {
     MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
 }
 
 bool
 js::InExclusiveParallelSection()
 {
     return false;
@@ -269,21 +257,16 @@ 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
@@ -330,17 +313,19 @@ class ForkJoinOperation
     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 determineBailoutCause();
+    void getBailoutScriptAndPc(MutableHandleScript bailoutScript, jsbytecode **bailoutPc,
+                               ParallelBailoutCause *bailoutCause);
+    bool reportBailoutWarnings();
     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
@@ -587,19 +572,16 @@ 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),
@@ -640,18 +622,20 @@ 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++)
-        bailoutRecords_[i].init(cx_);
+    for (uint32_t i = 0; i < numWorkers; i++) {
+        if (!bailoutRecords_[i].init(cx_))
+            return SpewEndOp(ExecutionFatal);
+    }
 
     if (enqueueInitialScript(&status) == RedLight)
         return SpewEndOp(status);
 
     Spew(SpewOps, "Execution mode: %s", ForkJoinModeString(mode_));
     switch (mode_) {
       case ForkJoinModeNormal:
       case ForkJoinModeCompile:
@@ -671,17 +655,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(cx_);
+            bailoutRecords_[i].reset();
 
         if (compileForParallelExecution(&status) == RedLight)
             return SpewEndOp(status);
 
         JS_ASSERT(worklist_.length() == 0);
         if (parallelExecution(&status) == RedLight)
             return SpewEndOp(status);
 
@@ -1098,96 +1082,195 @@ BailoutExplanation(ParallelBailoutCause 
 
 bool
 ForkJoinOperation::isInitialScript(HandleScript script)
 {
     return fun_->is<JSFunction>() && (fun_->as<JSFunction>().nonLazyScript() == script);
 }
 
 void
-ForkJoinOperation::determineBailoutCause()
+ForkJoinOperation::getBailoutScriptAndPc(MutableHandleScript bailoutScript, jsbytecode **bailoutPc,
+                                         ParallelBailoutCause *bailoutCause)
 {
-    bailoutCause = ParallelBailoutNone;
     for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
-        if (bailoutRecords_[i].cause == ParallelBailoutNone)
+        switch (bailoutRecords_[i].cause) {
+          case ParallelBailoutNone:
+          case ParallelBailoutInterrupt:
             continue;
+          default:
+            break;
+        }
 
-        if (bailoutRecords_[i].cause == ParallelBailoutInterrupt)
+        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)
+{
+    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);
+}
+
+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)
             continue;
 
-        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;
+        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));
 
-            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);
+            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()));
 
-            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);
+                // 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, unknown loc",
-                 i,
-                 bailoutCause);
+                    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);
+                }
+            }
         }
     }
+
+    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++) {
-        RootedScript script(cx_, bailoutRecords_[i].topScript);
+        switch (bailoutRecords_[i].cause) {
+          // No bailout.
+          case ParallelBailoutNone:
+            continue;
 
-        // No script to invalidate.
-        if (!script || !script->hasParallelIonScript())
+          // 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;
 
-        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 (hasScript(invalid, script))
+        if (!script->hasParallelIonScript() || 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;
@@ -1276,19 +1359,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();
 
-    SpewBailout(bailouts, bailoutScript, bailoutBytecode, bailoutCause);
+    if (!reportBailoutWarnings())
+        return fatalError(status);
 
     // 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
@@ -1342,18 +1425,17 @@ class ParallelIonInvoke
         JitCode *code = ion->method();
         jitcode_ = code->raw();
         enter_ = rt->jitRuntime()->enterIon();
         calleeToken_ = CalleeToToken(callee);
     }
 
     bool invoke(ForkJoinContext *cx) {
         JitActivation activation(cx);
-        // In-out parameter: on input it denotes the number of values to preserve after the call.
-        Value result = Int32Value(0);
+        Value result = Int32Value(argc_);
         CALL_GENERATED_CODE(enter_, jitcode_, argc_ + 1, argv_ + 1, nullptr, calleeToken_,
                             nullptr, 0, &result);
         return !result.isMagic();
     }
 };
 
 /////////////////////////////////////////////////////////////////////////////
 // ForkJoinShared
@@ -1561,35 +1643,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->topScript == nullptr);
+    JS_ASSERT(!cx.bailoutRecord->bailedOut());
 
     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->setCause(ParallelBailoutMainScriptNotPresent);
+        cx.bailoutRecord->joinCause(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->topScript);
+        JS_ASSERT(ok == !cx.bailoutRecord->bailedOut());
         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
@@ -1625,17 +1707,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->setCause(ParallelBailoutInterrupt);
+        cx.bailoutRecord->joinCause(ParallelBailoutInterrupt);
         setAbortFlagAndRequestInterrupt(false);
     }
 }
 
 void
 ForkJoinShared::setAbortFlagAndRequestInterrupt(bool fatal)
 {
     AutoLockMonitor lock(*this);
@@ -1799,99 +1881,108 @@ ForkJoinContext::check()
     }
     return true;
 }
 
 void
 ForkJoinContext::requestGC(JS::gcreason::Reason reason)
 {
     shared_->requestGC(reason);
-    bailoutRecord->setCause(ParallelBailoutRequestedGC);
+    bailoutRecord->joinCause(ParallelBailoutRequestedGC);
     shared_->setAbortFlagAndRequestInterrupt(false);
 }
 
 void
 ForkJoinContext::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
 {
     shared_->requestZoneGC(zone, reason);
-    bailoutRecord->setCause(ParallelBailoutRequestedZoneGC);
+    bailoutRecord->joinCause(ParallelBailoutRequestedZoneGC);
     shared_->setAbortFlagAndRequestInterrupt(false);
 }
 
 bool
 ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
 {
     shared_->setPendingAbortFatal();
-    bailoutRecord->setCause(cause);
+    bailoutRecord->joinCause(cause);
     return false;
 }
 
 //////////////////////////////////////////////////////////////////////////////
 // ParallelBailoutRecord
 
-void
-js::ParallelBailoutRecord::init(JSContext *cx)
+ParallelBailoutRecord::~ParallelBailoutRecord()
 {
-    reset(cx);
+    reset();
+    js_delete(frames_);
 }
 
-void
-js::ParallelBailoutRecord::reset(JSContext *cx)
+bool
+ParallelBailoutRecord::init(JSContext *cx)
 {
-    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);
+    MOZ_ASSERT(!frames_);
+    frames_ = cx->new_<Vector<RematerializedFrame *> >(cx);
+    return !!frames_;
 }
 
 void
-js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
-                                       JSScript *outermostScript,
-                                       JSScript *currentScript,
-                                       jsbytecode *currentPc)
+ParallelBailoutRecord::reset()
+{
+    RematerializedFrame::FreeInVector(frames());
+    cause = ParallelBailoutNone;
+}
+
+template <class T>
+static void
+RematerializeFramesWithIter(ForkJoinContext *cx, T &frameIter,
+                            Vector<RematerializedFrame *> &frames)
 {
-    JS_ASSERT_IF(outermostScript, currentScript);
-    JS_ASSERT_IF(outermostScript, outermostScript->hasParallelIonScript());
-    JS_ASSERT_IF(currentScript, outermostScript);
-    JS_ASSERT_IF(!currentScript, !currentPc);
+    // 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());
+
+    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;
-
-    if (currentScript)
-        addTrace(currentScript, currentPc);
+        // Reverse the inline frames into the main vector.
+        while (!inlineFrames.empty()) {
+            if (!frames.append(inlineFrames.popCopy())) {
+                RematerializedFrame::FreeInVector(inlineFrames);
+                RematerializedFrame::FreeInVector(frames);
+                return;
+            }
+        }
+    }
 }
 
 void
-js::ParallelBailoutRecord::addTrace(JSScript *script,
-                                    jsbytecode *pc)
+ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, JitFrameIterator &frameIter)
 {
-    // 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;
+    RematerializeFramesWithIter(cx, frameIter, frames());
+}
 
-    if (depth < MaxDepth) {
-        trace[depth].script = script;
-        trace[depth].bytecode = pc;
-        depth += 1;
-    }
+void
+ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, IonBailoutIterator &frameIter)
+{
+    RematerializeFramesWithIter(cx, frameIter, frames());
 }
 
 //////////////////////////////////////////////////////////////////////////////
 
 //
 // Debug spew
 //
 
@@ -2155,31 +2246,16 @@ 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)
 {
@@ -2238,22 +2314,16 @@ 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,103 +267,116 @@ 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,
+    ParallelBailoutNone = 0,
+
+    ParallelBailoutUnsupported,
+    ParallelBailoutUnsupportedVM,
+
+    // The periodic interrupt failed, which can mean that either
+    // another thread canceled, the user interrupted us, etc
+    ParallelBailoutInterrupt,
 
     // Compiler returned Method_Skipped
     ParallelBailoutCompilationSkipped,
 
     // Compiler returned Method_CantCompile
     ParallelBailoutCompilationFailure,
 
-    // The periodic interrupt failed, which can mean that either
-    // another thread canceled, the user interrupted us, etc
-    ParallelBailoutInterrupt,
+    // Propagating a failure, i.e., another thread requested the computation
+    // be aborted.
+    ParallelBailoutPropagate,
 
     // 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
 };
 
-struct ParallelBailoutTrace {
-    JSScript *script;
-    jsbytecode *bytecode;
-};
+namespace jit {
+class BailoutStack;
+class JitFrameIterator;
+class RematerializedFrame;
+}
 
 // See "Bailouts" section in comment above.
-struct ParallelBailoutRecord {
-    JSScript *topScript;
+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_;
     ParallelBailoutCause cause;
 
-    // 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];
+    ParallelBailoutRecord()
+      : frames_(nullptr),
+        cause(ParallelBailoutNone)
+    { }
+
+    ~ParallelBailoutRecord();
+
+    bool init(JSContext *cx);
+    void reset();
 
-    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);
+    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);
 };
 
 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
@@ -574,33 +587,31 @@ 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()