Bug 921325 - Return bool from PJS VM functions and use the fatal bit on ForkJoinSlice to signal fatal vs retry. (r=nmatsakis)
authorShu-yu Guo <shu@rfrn.org>
Thu, 10 Oct 2013 20:02:32 -0700
changeset 150428 6fef99317f215e9b7b4830100f1d5989330a47b2
parent 150427 7924e5fb323a95cc1064cac1ef18572f9c9c7d54
child 150429 9f2f57672cc37dd0e6ecff324ac9b0bfb66227b8
child 150454 80ceb04b7183a8bb5f5f589559d2b5831040cfb6
push id2993
push usercbook@mozilla.com
push dateFri, 11 Oct 2013 09:22:00 +0000
treeherderfx-team@fe160ec1e0ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnmatsakis
bugs921325
milestone27.0a1
Bug 921325 - Return bool from PJS VM functions and use the fatal bit on ForkJoinSlice to signal fatal vs retry. (r=nmatsakis)
js/public/CallArgs.h
js/src/jit/CodeGenerator.cpp
js/src/jit/IonCaches.cpp
js/src/jit/IonCaches.h
js/src/jit/IonFrames.cpp
js/src/jit/ParallelFunctions.cpp
js/src/jit/ParallelFunctions.h
js/src/jit/VMFunctions.h
js/src/jit/arm/Trampoline-arm.cpp
js/src/jit/x64/Trampoline-x64.cpp
js/src/jit/x86/Trampoline-x86.cpp
js/src/jsarray.cpp
js/src/jscntxt.cpp
js/src/jscntxtinlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/vm/ForkJoin.cpp
js/src/vm/ForkJoin.h
--- a/js/public/CallArgs.h
+++ b/js/public/CallArgs.h
@@ -38,17 +38,17 @@
 #include "js/RootingAPI.h"
 #include "js/Value.h"
 
 /* Typedef for native functions called by the JS VM. */
 typedef bool
 (* JSNative)(JSContext *cx, unsigned argc, JS::Value *vp);
 
 /* Typedef for native functions that may be called in parallel. */
-typedef js::ParallelResult
+typedef bool
 (* JSParallelNative)(js::ForkJoinSlice *slice, unsigned argc, JS::Value *vp);
 
 /*
  * Typedef for native functions that may be called either in parallel or
  * sequential execution.
  */
 typedef bool
 (* JSThreadSafeNative)(js::ThreadSafeContext *cx, unsigned argc, JS::Value *vp);
@@ -57,17 +57,17 @@ typedef bool
  * Convenience wrappers for passing in ThreadSafeNative to places that expect
  * a JSNative or a JSParallelNative.
  */
 template <JSThreadSafeNative threadSafeNative>
 inline bool
 JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp);
 
 template <JSThreadSafeNative threadSafeNative>
-inline js::ParallelResult
+inline bool
 JSParallelNativeThreadSafeWrapper(js::ForkJoinSlice *slice, unsigned argc, JS::Value *vp);
 
 /*
  * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
  * replacing with the global object as necessary.
  *
  * This method will go away at some point: instead use |args.thisv()|.  If the
  * value is an object, no further work is required.  If that value is |null| or
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -149,17 +149,17 @@ CodeGenerator::CodeGenerator(MIRGenerato
 
 CodeGenerator::~CodeGenerator()
 {
     JS_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteLinks() == 0);
     js_delete(unassociatedScriptCounts_);
 }
 
 typedef bool (*StringToNumberFn)(ThreadSafeContext *, JSString *, double *);
-typedef ParallelResult (*StringToNumberParFn)(ForkJoinSlice *, JSString *, double *);
+typedef bool (*StringToNumberParFn)(ForkJoinSlice *, JSString *, double *);
 static const VMFunctionsModal StringToNumberInfo = VMFunctionsModal(
     FunctionInfo<StringToNumberFn>(StringToNumber),
     FunctionInfo<StringToNumberParFn>(StringToNumberPar));
 
 bool
 CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
 {
     ValueOperand operand = ToValue(lir, LValueToInt32::Input);
@@ -644,17 +644,17 @@ CodeGenerator::visitTypeObjectDispatch(L
 
     // Unknown function: jump to fallback block.
     LBlock *fallback = mir->getFallback()->lir();
     masm.jump(fallback->label());
     return true;
 }
 
 typedef JSFlatString *(*IntToStringFn)(ThreadSafeContext *, int);
-typedef ParallelResult (*IntToStringParFn)(ForkJoinSlice *, int, MutableHandleString);
+typedef JSFlatString *(*IntToStringParFn)(ForkJoinSlice *, int);
 static const VMFunctionsModal IntToStringInfo = VMFunctionsModal(
     FunctionInfo<IntToStringFn>(Int32ToString<CanGC>),
     FunctionInfo<IntToStringParFn>(IntToStringPar));
 
 bool
 CodeGenerator::visitIntToString(LIntToString *lir)
 {
     Register input = ToRegister(lir->input());
@@ -671,17 +671,17 @@ CodeGenerator::visitIntToString(LIntToSt
     masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings.intStaticTable), output);
     masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 typedef JSString *(*DoubleToStringFn)(ThreadSafeContext *, double);
-typedef ParallelResult (*DoubleToStringParFn)(ForkJoinSlice *, double, MutableHandleString);
+typedef JSString *(*DoubleToStringParFn)(ForkJoinSlice *, double);
 static const VMFunctionsModal DoubleToStringInfo = VMFunctionsModal(
     FunctionInfo<DoubleToStringFn>(NumberToString<CanGC>),
     FunctionInfo<DoubleToStringParFn>(DoubleToStringPar));
 
 bool
 CodeGenerator::visitDoubleToString(LDoubleToString *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
@@ -3857,17 +3857,17 @@ CodeGenerator::visitModD(LModD *ins)
     if (gen->compilingAsmJS())
         masm.callWithABI(AsmJSImm_ModD, MacroAssembler::DOUBLE);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MacroAssembler::DOUBLE);
     return true;
 }
 
 typedef bool (*BinaryFn)(JSContext *, MutableHandleValue, MutableHandleValue, Value *);
-typedef ParallelResult (*BinaryParFn)(ForkJoinSlice *, HandleValue, HandleValue, Value *);
+typedef bool (*BinaryParFn)(ForkJoinSlice *, HandleValue, HandleValue, Value *);
 
 static const VMFunction AddInfo = FunctionInfo<BinaryFn>(js::AddValues);
 static const VMFunction SubInfo = FunctionInfo<BinaryFn>(js::SubValues);
 static const VMFunction MulInfo = FunctionInfo<BinaryFn>(js::MulValues);
 static const VMFunction DivInfo = FunctionInfo<BinaryFn>(js::DivValues);
 static const VMFunction ModInfo = FunctionInfo<BinaryFn>(js::ModValues);
 static const VMFunctionsModal UrshInfo = VMFunctionsModal(
     FunctionInfo<BinaryFn>(js::UrshValues),
@@ -3899,17 +3899,17 @@ CodeGenerator::visitBinaryV(LBinaryV *li
         return callVM(UrshInfo, lir);
 
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected binary op");
     }
 }
 
 typedef bool (*StringCompareFn)(JSContext *, HandleString, HandleString, bool *);
-typedef ParallelResult (*StringCompareParFn)(ForkJoinSlice *, HandleString, HandleString, bool *);
+typedef bool (*StringCompareParFn)(ForkJoinSlice *, HandleString, HandleString, bool *);
 static const VMFunctionsModal StringsEqualInfo = VMFunctionsModal(
     FunctionInfo<StringCompareFn>(jit::StringsEqual<true>),
     FunctionInfo<StringCompareParFn>(jit::StringsEqualPar));
 static const VMFunctionsModal StringsNotEqualInfo = VMFunctionsModal(
     FunctionInfo<StringCompareFn>(jit::StringsEqual<false>),
     FunctionInfo<StringCompareParFn>(jit::StringsUnequalPar));
 
 bool
@@ -3971,18 +3971,17 @@ CodeGenerator::visitCompareS(LCompareS *
     Register right = ToRegister(lir->right());
     Register output = ToRegister(lir->output());
     Register temp = ToRegister(lir->temp());
 
     return emitCompareS(lir, op, left, right, output, temp);
 }
 
 typedef bool (*CompareFn)(JSContext *, MutableHandleValue, MutableHandleValue, bool *);
-typedef ParallelResult (*CompareParFn)(ForkJoinSlice *, MutableHandleValue, MutableHandleValue,
-                                       bool *);
+typedef bool (*CompareParFn)(ForkJoinSlice *, MutableHandleValue, MutableHandleValue, bool *);
 static const VMFunctionsModal EqInfo = VMFunctionsModal(
     FunctionInfo<CompareFn>(jit::LooselyEqual<true>),
     FunctionInfo<CompareParFn>(jit::LooselyEqualPar));
 static const VMFunctionsModal NeInfo = VMFunctionsModal(
     FunctionInfo<CompareFn>(jit::LooselyEqual<false>),
     FunctionInfo<CompareParFn>(jit::LooselyUnequalPar));
 static const VMFunctionsModal StrictEqInfo = VMFunctionsModal(
     FunctionInfo<CompareFn>(jit::StrictlyEqual<true>),
@@ -4255,18 +4254,17 @@ CodeGenerator::visitEmulatesUndefinedAnd
 
     Register objreg = ToRegister(lir->input());
 
     testObjectTruthy(objreg, unequal, equal, ToRegister(lir->temp()), ool);
     return true;
 }
 
 typedef JSString *(*ConcatStringsFn)(ThreadSafeContext *, HandleString, HandleString);
-typedef ParallelResult (*ConcatStringsParFn)(ForkJoinSlice *, HandleString, HandleString,
-                                             MutableHandleString);
+typedef JSString *(*ConcatStringsParFn)(ForkJoinSlice *, HandleString, HandleString);
 static const VMFunctionsModal ConcatStringsInfo = VMFunctionsModal(
     FunctionInfo<ConcatStringsFn>(ConcatStrings<CanGC>),
     FunctionInfo<ConcatStringsParFn>(ConcatStringsPar));
 
 bool
 CodeGenerator::emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output)
 {
     OutOfLineCode *ool = oolCallVM(ConcatStringsInfo, lir, (ArgList(), lhs, rhs),
@@ -4848,18 +4846,17 @@ CodeGenerator::visitStoreElementHoleV(LS
         masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 typedef bool (*SetObjectElementFn)(JSContext *, HandleObject, HandleValue, HandleValue,
                                    bool strict);
-typedef ParallelResult (*SetElementParFn)(ForkJoinSlice *, HandleObject, HandleValue,
-                                          HandleValue, bool);
+typedef bool (*SetElementParFn)(ForkJoinSlice *, HandleObject, HandleValue, HandleValue, bool);
 static const VMFunctionsModal SetObjectElementInfo = VMFunctionsModal(
     FunctionInfo<SetObjectElementFn>(SetObjectElement),
     FunctionInfo<SetElementParFn>(SetElementPar));
 
 bool
 CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
 {
     Register object, elements;
@@ -5456,19 +5453,18 @@ CodeGenerator::visitRunOncePrologue(LRun
 {
     pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
     return callVM(RunOnceScriptPrologueInfo, lir);
 }
 
 
 typedef JSObject *(*InitRestParameterFn)(JSContext *, uint32_t, Value *, HandleObject,
                                          HandleObject);
-typedef ParallelResult (*InitRestParameterParFn)(ForkJoinSlice *, uint32_t, Value *,
-                                                 HandleObject, HandleObject,
-                                                 MutableHandleObject);
+typedef JSObject *(*InitRestParameterParFn)(ForkJoinSlice *, uint32_t, Value *,
+                                            HandleObject, HandleObject);
 static const VMFunctionsModal InitRestParameterInfo = VMFunctionsModal(
     FunctionInfo<InitRestParameterFn>(InitRestParameter),
     FunctionInfo<InitRestParameterParFn>(InitRestParameterPar));
 
 bool
 CodeGenerator::emitRest(LInstruction *lir, Register array, Register numActuals,
                         Register temp0, Register temp1, unsigned numFormals,
                         JSObject *templateObject)
@@ -6124,18 +6120,17 @@ CodeGenerator::visitGetPropertyIC(OutOfL
         return false;
     StoreValueTo(ic->output()).generate(this);
     restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
 
     masm.jump(ool->rejoin());
     return true;
 }
 
-typedef ParallelResult (*GetPropertyParICFn)(ForkJoinSlice *, size_t, HandleObject,
-                                             MutableHandleValue);
+typedef bool (*GetPropertyParICFn)(ForkJoinSlice *, size_t, HandleObject, MutableHandleValue);
 const VMFunction GetPropertyParIC::UpdateInfo =
     FunctionInfo<GetPropertyParICFn>(GetPropertyParIC::update);
 
 bool
 CodeGenerator::visitGetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyParIC> &ic)
 {
     LInstruction *lir = ool->lir();
     saveLive(lir);
@@ -6261,18 +6256,17 @@ CodeGenerator::visitSetElementIC(OutOfLi
     if (!callVM(SetElementIC::UpdateInfo, lir))
         return false;
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
     return true;
 }
 
-typedef ParallelResult (*SetElementParICFn)(ForkJoinSlice *, size_t, HandleObject,
-                                            HandleValue, HandleValue);
+typedef bool (*SetElementParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue, HandleValue);
 const VMFunction SetElementParIC::UpdateInfo =
     FunctionInfo<SetElementParICFn>(SetElementParIC::update);
 
 bool
 CodeGenerator::visitSetElementParIC(OutOfLineUpdateCache *ool, DataPtr<SetElementParIC> &ic)
 {
     LInstruction *lir = ool->lir();
     saveLive(lir);
@@ -6284,18 +6278,18 @@ CodeGenerator::visitSetElementParIC(OutO
     if (!callVM(SetElementParIC::UpdateInfo, lir))
         return false;
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
     return true;
 }
 
-typedef ParallelResult (*GetElementParICFn)(ForkJoinSlice *, size_t, HandleObject,
-                                            HandleValue, MutableHandleValue);
+typedef bool (*GetElementParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue,
+                                  MutableHandleValue);
 const VMFunction GetElementParIC::UpdateInfo =
     FunctionInfo<GetElementParICFn>(GetElementParIC::update);
 
 bool
 CodeGenerator::visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic)
 {
     LInstruction *lir = ool->lir();
     saveLive(lir);
@@ -6340,19 +6334,18 @@ CodeGenerator::visitBindNameIC(OutOfLine
     restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered());
 
     masm.jump(ool->rejoin());
     return true;
 }
 
 typedef bool (*SetPropertyFn)(JSContext *, HandleObject,
                               HandlePropertyName, const HandleValue, bool, jsbytecode *);
-typedef ParallelResult (*SetPropertyParFn)(ForkJoinSlice *, HandleObject,
-                                           HandlePropertyName, const HandleValue, bool,
-                                           jsbytecode *);
+typedef bool (*SetPropertyParFn)(ForkJoinSlice *, HandleObject,
+                                 HandlePropertyName, const HandleValue, bool, jsbytecode *);
 static const VMFunctionsModal SetPropertyInfo = VMFunctionsModal(
     FunctionInfo<SetPropertyFn>(SetProperty),
     FunctionInfo<SetPropertyParFn>(SetPropertyPar));
 
 bool
 CodeGenerator::visitCallSetProperty(LCallSetProperty *ins)
 {
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LCallSetProperty::Value));
@@ -6448,17 +6441,17 @@ CodeGenerator::visitSetPropertyIC(OutOfL
     if (!callVM(SetPropertyIC::UpdateInfo, lir))
         return false;
     restoreLive(lir);
 
     masm.jump(ool->rejoin());
     return true;
 }
 
-typedef ParallelResult (*SetPropertyParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue);
+typedef bool (*SetPropertyParICFn)(ForkJoinSlice *, size_t, HandleObject, HandleValue);
 const VMFunction SetPropertyParIC::UpdateInfo =
     FunctionInfo<SetPropertyParICFn>(SetPropertyParIC::update);
 
 bool
 CodeGenerator::visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic)
 {
     LInstruction *lir = ool->lir();
     saveLive(lir);
@@ -6480,30 +6473,30 @@ static const VMFunction ThrowInfo = Func
 bool
 CodeGenerator::visitThrow(LThrow *lir)
 {
     pushArg(ToValue(lir, LThrow::Value));
     return callVM(ThrowInfo, lir);
 }
 
 typedef bool (*BitNotFn)(JSContext *, HandleValue, int *p);
-typedef ParallelResult (*BitNotParFn)(ForkJoinSlice *, HandleValue, int32_t *);
+typedef bool (*BitNotParFn)(ForkJoinSlice *, HandleValue, int32_t *);
 static const VMFunctionsModal BitNotInfo = VMFunctionsModal(
     FunctionInfo<BitNotFn>(BitNot),
     FunctionInfo<BitNotParFn>(BitNotPar));
 
 bool
 CodeGenerator::visitBitNotV(LBitNotV *lir)
 {
     pushArg(ToValue(lir, LBitNotV::Input));
     return callVM(BitNotInfo, lir);
 }
 
 typedef bool (*BitopFn)(JSContext *, HandleValue, HandleValue, int *p);
-typedef ParallelResult (*BitopParFn)(ForkJoinSlice *, HandleValue, HandleValue, int32_t *);
+typedef bool (*BitopParFn)(ForkJoinSlice *, HandleValue, HandleValue, int32_t *);
 static const VMFunctionsModal BitAndInfo = VMFunctionsModal(
     FunctionInfo<BitopFn>(BitAnd),
     FunctionInfo<BitopParFn>(BitAndPar));
 static const VMFunctionsModal BitOrInfo = VMFunctionsModal(
     FunctionInfo<BitopFn>(BitOr),
     FunctionInfo<BitopParFn>(BitOrPar));
 static const VMFunctionsModal BitXorInfo = VMFunctionsModal(
     FunctionInfo<BitopFn>(BitXor),
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1846,83 +1846,83 @@ GetPropertyParIC::attachTypedArrayLength
     DispatchStubPrepender attacher(*this);
     GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output());
 
     JS_ASSERT(!hasTypedArrayLengthStub_);
     hasTypedArrayLengthStub_ = true;
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array length");
 }
 
-ParallelResult
+bool
 GetPropertyParIC::update(ForkJoinSlice *slice, size_t cacheIndex,
                          HandleObject obj, MutableHandleValue vp)
 {
     AutoFlushCache afc("GetPropertyParCache", slice->runtime()->ionRuntime());
 
     IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript();
     GetPropertyParIC &cache = ion->getCache(cacheIndex).toGetPropertyPar();
 
     // Grab the property early, as the pure path is fast anyways and doesn't
     // need a lock. If we can't do it purely, bail out of parallel execution.
     if (!GetPropertyPure(slice, obj, NameToId(cache.name()), vp.address()))
-        return TP_RETRY_SEQUENTIALLY;
+        return false;
 
     // Avoid unnecessary locking if cannot attach stubs.
     if (!cache.canAttachStub())
-        return TP_SUCCESS;
+        return true;
 
     {
         // Lock the context before mutating the cache. Ideally we'd like to do
         // finer-grained locking, with one lock per cache. However, generating
         // new jitcode uses a global ExecutableAllocator tied to the runtime.
         LockedJSContext cx(slice);
 
         if (cache.canAttachStub()) {
             bool alreadyStubbed;
             if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed))
-                return TP_FATAL;
+                return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
             if (alreadyStubbed)
-                return TP_SUCCESS;
+                return true;
 
             // See note about the stub limit in GetPropertyCache.
             bool attachedStub = false;
 
             {
                 RootedShape shape(cx);
                 RootedObject holder(cx);
                 RootedPropertyName name(cx, cache.name());
 
                 GetPropertyIC::NativeGetPropCacheability canCache =
                     CanAttachNativeGetProp(cx, cache, obj, name, &holder, &shape);
 
                 if (canCache == GetPropertyIC::CanAttachReadSlot) {
                     if (!cache.attachReadSlot(cx, ion, obj, holder, shape))
-                        return TP_FATAL;
+                        return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
                     attachedStub = true;
                 }
 
                 if (!attachedStub && canCache == GetPropertyIC::CanAttachArrayLength) {
                     if (!cache.attachArrayLength(cx, ion, obj))
-                        return TP_FATAL;
+                        return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
                     attachedStub = true;
                 }
             }
 
             if (!attachedStub && !cache.hasTypedArrayLengthStub() &&
                 obj->is<TypedArrayObject>() && slice->names().length == cache.name() &&
                 (cache.output().type() == MIRType_Value || cache.output().type() == MIRType_Int32))
             {
                 if (!cache.attachTypedArrayLength(cx, ion, obj))
-                    return TP_FATAL;
+                    return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
         }
     }
 
-    return TP_SUCCESS;
+    return true;
 }
 
 void
 IonCache::disable()
 {
     reset();
     this->disabled_ = 1;
 }
@@ -2835,96 +2835,88 @@ SetPropertyIC::update(JSContext *cx, siz
 
 void
 SetPropertyIC::reset()
 {
     RepatchIonCache::reset();
     hasGenericProxyStub_ = false;
 }
 
-ParallelResult
+bool
 SetPropertyParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
                          HandleValue value)
 {
     JS_ASSERT(slice->isThreadLocal(obj));
 
     AutoFlushCache afc("SetPropertyParCache", slice->runtime()->ionRuntime());
 
     IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript();
     SetPropertyParIC &cache = ion->getCache(cacheIndex).toSetPropertyPar();
 
     RootedValue v(slice, value);
     RootedId id(slice, AtomToId(cache.name()));
 
     // Avoid unnecessary locking if cannot attach stubs.
     if (!cache.canAttachStub()) {
-        if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0,
-                                                           &v, cache.strict()))
-        {
-            return TP_RETRY_SEQUENTIALLY;
-        }
-        return TP_SUCCESS;
+        return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0,
+                                                             &v, cache.strict());
     }
 
     SetPropertyIC::NativeSetPropCacheability canCache = SetPropertyIC::CanAttachNone;
     bool attachedStub = false;
 
     {
         LockedJSContext cx(slice);
 
         if (cache.canAttachStub()) {
             bool alreadyStubbed;
             if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed))
-                return TP_FATAL;
+                return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
             if (alreadyStubbed) {
-                if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0,
-                                                                   &v, cache.strict()))
-                {
-                    return TP_RETRY_SEQUENTIALLY;
-                }
-                return TP_SUCCESS;
+                return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0,
+                                                                     &v, cache.strict());
             }
 
             // If the object has a lazy type, we need to de-lazify it, but
             // this is not safe in parallel.
             if (obj->hasLazyType())
-                return TP_RETRY_SEQUENTIALLY;
+                return false;
 
             {
                 RootedShape shape(slice);
                 RootedObject holder(slice);
                 bool checkTypeset;
                 canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(),
                                                   &holder, &shape, &checkTypeset);
 
                 if (canCache == SetPropertyIC::CanAttachSetSlot) {
                     if (!cache.attachSetSlot(cx, ion, obj, shape, checkTypeset))
-                        return TP_FATAL;
+                        return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
                     attachedStub = true;
                 }
             }
         }
     }
 
     uint32_t oldSlots = obj->numDynamicSlots();
     RootedShape oldShape(slice, obj->lastProperty());
 
     if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, cache.strict()))
-        return TP_RETRY_SEQUENTIALLY;
+        return false;
 
     if (!attachedStub && canCache == SetPropertyIC::MaybeCanAttachAddSlot &&
         !cache.needsTypeBarrier() &&
         IsPropertyAddInlineable(obj, id, oldSlots, oldShape))
     {
         LockedJSContext cx(slice);
         if (cache.canAttachStub() && !cache.attachAddSlot(cx, ion, obj, oldShape))
-            return TP_FATAL;
+            return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
     }
 
-    return TP_SUCCESS;
+    return true;
 }
 
 bool
 SetPropertyParIC::attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape,
                                   bool checkTypeset)
 {
     MacroAssembler masm(cx);
     DispatchStubPrepender attacher(*this);
@@ -3688,47 +3680,47 @@ SetElementParIC::attachTypedArrayElement
                                       tempToUnboxIndex(), temp(), tempFloat()))
     {
         return false;
     }
 
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
 }
 
-ParallelResult
+bool
 SetElementParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
                         HandleValue idval, HandleValue value)
 {
     IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript();
     SetElementParIC &cache = ion->getCache(cacheIndex).toSetElementPar();
 
     // Avoid unnecessary locking if cannot attach stubs.
     if (!cache.canAttachStub())
         return SetElementPar(slice, obj, idval, value, cache.strict());
 
     {
         LockedJSContext cx(slice);
 
         if (cache.canAttachStub()) {
             bool alreadyStubbed;
             if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed))
-                return TP_FATAL;
+                return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
             if (alreadyStubbed)
                 return SetElementPar(slice, obj, idval, value, cache.strict());
 
             bool attachedStub = false;
             if (IsDenseElementSetInlineable(obj, idval)) {
                 if (!cache.attachDenseElement(cx, ion, obj, idval))
-                    return TP_FATAL;
+                    return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
             if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
                 TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
                 if (!cache.attachTypedArrayElement(cx, ion, tarr))
-                    return TP_FATAL;
+                    return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
             }
         }
     }
 
     return SetElementPar(slice, obj, idval, value, cache.strict());
 }
 
 bool
@@ -3767,84 +3759,84 @@ GetElementParIC::attachTypedArrayElement
                                          TypedArrayObject *tarr, const Value &idval)
 {
     MacroAssembler masm(cx);
     DispatchStubPrepender attacher(*this);
     GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
 }
 
-ParallelResult
+bool
 GetElementParIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
                         HandleValue idval, MutableHandleValue vp)
 {
     AutoFlushCache afc("GetElementParCache", slice->runtime()->ionRuntime());
 
     IonScript *ion = GetTopIonJSScript(slice)->parallelIonScript();
     GetElementParIC &cache = ion->getCache(cacheIndex).toGetElementPar();
 
     // Try to get the element early, as the pure path doesn't need a lock. If
     // we can't do it purely, bail out of parallel execution.
     if (!GetObjectElementOperationPure(slice, obj, idval, vp.address()))
-        return TP_RETRY_SEQUENTIALLY;
+        return false;
 
     // Avoid unnecessary locking if cannot attach stubs.
     if (!cache.canAttachStub())
-        return TP_SUCCESS;
+        return true;
 
     {
         LockedJSContext cx(slice);
 
         if (cache.canAttachStub()) {
             bool alreadyStubbed;
             if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed))
-                return TP_FATAL;
+                return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
             if (alreadyStubbed)
-                return TP_SUCCESS;
+                return true;
 
             jsid id;
             if (!ValueToIdPure(idval, &id))
-                return TP_FATAL;
+                return false;
 
             bool attachedStub = false;
             if (cache.monitoredResult() &&
                 GetElementIC::canAttachGetProp(obj, idval, id))
             {
                 RootedShape shape(cx);
                 RootedObject holder(cx);
                 RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
 
                 GetPropertyIC::NativeGetPropCacheability canCache =
                     CanAttachNativeGetProp(cx, cache, obj, name, &holder, &shape);
 
                 if (canCache == GetPropertyIC::CanAttachReadSlot)
                 {
                     if (!cache.attachReadSlot(cx, ion, obj, idval, name, holder, shape))
-                        return TP_FATAL;
+                        return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
                     attachedStub = true;
                 }
             }
             if (!attachedStub &&
                 GetElementIC::canAttachDenseElement(obj, idval))
             {
                 if (!cache.attachDenseElement(cx, ion, obj, idval))
-                    return TP_FATAL;
+                    return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
             if (!attachedStub &&
                 GetElementIC::canAttachTypedArrayElement(obj, idval, cache.output()))
             {
                 if (!cache.attachTypedArrayElement(cx, ion, &obj->as<TypedArrayObject>(), idval))
-                    return TP_FATAL;
+                    return slice->setPendingAbortFatal(ParallelBailoutFailedIC);
                 attachedStub = true;
             }
         }
     }
 
-    return TP_SUCCESS;
+    return true;
 }
 
 bool
 BindNameIC::attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain)
 {
     JS_ASSERT(scopeChain->is<GlobalObject>());
 
     MacroAssembler masm(cx);
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -1050,18 +1050,18 @@ class GetPropertyParIC : public Parallel
     bool allowGetters() const { return false; }
     bool allowArrayLength(Context, HandleObject) const { return true; }
 
     bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, JSObject *holder,
                         Shape *shape);
     bool attachArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj);
     bool attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj);
 
-    static ParallelResult update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
-                                 MutableHandleValue vp);
+    static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+                       MutableHandleValue vp);
 };
 
 class GetElementParIC : public ParallelIonCache
 {
   protected:
     Register object_;
     ConstantOrRegister index_;
     TypedOrValueRegister output_;
@@ -1106,18 +1106,18 @@ class GetElementParIC : public ParallelI
     bool allowArrayLength(Context, HandleObject) const { return false; }
 
     bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval,
                         PropertyName *name, JSObject *holder, Shape *shape);
     bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval);
     bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr,
                                  const Value &idval);
 
-    static ParallelResult update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, HandleValue idval,
-                                 MutableHandleValue vp);
+    static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, HandleValue idval,
+                       MutableHandleValue vp);
 
 };
 
 class SetPropertyParIC : public ParallelIonCache
 {
   protected:
     Register object_;
     PropertyName *name_;
@@ -1159,18 +1159,18 @@ class SetPropertyParIC : public Parallel
     bool needsTypeBarrier() const {
         return needsTypeBarrier_;
     }
 
     bool attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape,
                        bool checkTypeset);
     bool attachAddSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *oldShape);
 
-    static ParallelResult update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
-                                 HandleValue value);
+    static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+                       HandleValue value);
 };
 
 class SetElementParIC : public ParallelIonCache
 {
   protected:
     Register object_;
     Register tempToUnboxIndex_;
     Register temp_;
@@ -1221,18 +1221,18 @@ class SetElementParIC : public ParallelI
     }
     bool strict() const {
         return strict_;
     }
 
     bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval);
     bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr);
 
-    static ParallelResult update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
-                                 HandleValue idval, HandleValue value);
+    static bool update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
+                       HandleValue idval, HandleValue value);
 };
 
 #undef CACHE_HEADER
 
 // Implement cache casts now that the compiler can see the inheritance.
 #define CACHE_CASTS(ickind)                                             \
     ickind##IC &IonCache::to##ickind()                                  \
     {                                                                   \
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -622,18 +622,18 @@ HandleParallelFailure(ResumeFromExceptio
 {
     ForkJoinSlice *slice = ForkJoinSlice::Current();
     IonFrameIterator iter(slice->perThreadData->ionTop, ParallelExecution);
 
     parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry");
 
     while (!iter.isEntry()) {
         if (iter.isScripted()) {
-            slice->bailoutRecord->setCause(ParallelBailoutFailedIC,
-                                           iter.script(), iter.script(), nullptr);
+            slice->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM,
+                                              iter.script(), iter.script(), nullptr);
             break;
         }
         ++iter;
     }
 
     while (!iter.isEntry()) {
         if (iter.isScripted())
             PropagateAbortPar(iter.script(), iter.script());
--- a/js/src/jit/ParallelFunctions.cpp
+++ b/js/src/jit/ParallelFunctions.cpp
@@ -120,18 +120,17 @@ jit::CheckOverRecursedPar(ForkJoinSlice 
 
     uintptr_t realStackLimit;
     if (slice->isMainThread())
         realStackLimit = GetNativeStackLimit(slice);
     else
         realStackLimit = slice->perThreadData->ionStackLimit;
 
     if (!JS_CHECK_STACK_SIZE(realStackLimit, &stackDummy_)) {
-        slice->bailoutRecord->setCause(ParallelBailoutOverRecursed,
-                                       nullptr, nullptr, nullptr);
+        slice->bailoutRecord->setCause(ParallelBailoutOverRecursed);
         return false;
     }
 
     return CheckInterruptPar(slice);
 }
 
 bool
 jit::CheckInterruptPar(ForkJoinSlice *slice)
@@ -154,94 +153,77 @@ jit::ExtendArrayPar(ForkJoinSlice *slice
 {
     JSObject::EnsureDenseResult res =
         array->ensureDenseElementsPreservePackedFlag(slice, 0, length);
     if (res != JSObject::ED_OK)
         return nullptr;
     return array;
 }
 
-ParallelResult
+bool
 jit::SetPropertyPar(ForkJoinSlice *slice, HandleObject obj, HandlePropertyName name,
                     HandleValue value, bool strict, jsbytecode *pc)
 {
     JS_ASSERT(slice->isThreadLocal(obj));
 
     if (*pc == JSOP_SETALIASEDVAR) {
         // See comment in jit::SetProperty.
         Shape *shape = obj->nativeLookupPure(name);
         JS_ASSERT(shape && shape->hasSlot());
-        return obj->nativeSetSlotIfHasType(shape, value) ? TP_SUCCESS : TP_RETRY_SEQUENTIALLY;
+        return obj->nativeSetSlotIfHasType(shape, value);
     }
 
     // Fail early on hooks.
     if (obj->getOps()->setProperty)
         return TP_RETRY_SEQUENTIALLY;
 
     RootedValue v(slice, value);
     RootedId id(slice, NameToId(name));
-    if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, strict))
-        return TP_RETRY_SEQUENTIALLY;
-    return TP_SUCCESS;
+    return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, strict);
 }
 
-ParallelResult
+bool
 jit::SetElementPar(ForkJoinSlice *slice, HandleObject obj, HandleValue index, HandleValue value,
                    bool strict)
 {
     RootedId id(slice);
     if (!ValueToIdPure(index, id.address()))
-        return TP_RETRY_SEQUENTIALLY;
+        return false;
 
     // SetObjectElementOperation, the sequential version, has several checks
     // for certain deoptimizing behaviors, such as marking having written to
     // holes and non-indexed element accesses. We don't do that here, as we
     // can't modify any TI state anyways. If we need to add a new type, we
     // would bail out.
     RootedValue v(slice, value);
-    if (!baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, strict))
-        return TP_RETRY_SEQUENTIALLY;
-    return TP_SUCCESS;
+    return baseops::SetPropertyHelper<ParallelExecution>(slice, obj, obj, id, 0, &v, strict);
 }
 
-ParallelResult
-jit::ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right,
-                      MutableHandleString out)
+JSString *
+jit::ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right)
 {
-    JSString *str = ConcatStrings<NoGC>(slice, left, right);
-    if (!str)
-        return TP_RETRY_SEQUENTIALLY;
-    out.set(str);
-    return TP_SUCCESS;
+    return ConcatStrings<NoGC>(slice, left, right);
 }
 
-ParallelResult
-jit::IntToStringPar(ForkJoinSlice *slice, int i, MutableHandleString out)
+JSFlatString *
+jit::IntToStringPar(ForkJoinSlice *slice, int i)
 {
-    JSFlatString *str = Int32ToString<NoGC>(slice, i);
-    if (!str)
-        return TP_RETRY_SEQUENTIALLY;
-    out.set(str);
-    return TP_SUCCESS;
+    return Int32ToString<NoGC>(slice, i);
 }
 
-ParallelResult
-jit::DoubleToStringPar(ForkJoinSlice *slice, double d, MutableHandleString out)
+JSString *
+jit::DoubleToStringPar(ForkJoinSlice *slice, double d)
 {
-    JSString *str = NumberToString<NoGC>(slice, d);
-    if (!str)
-        return TP_RETRY_SEQUENTIALLY;
-    out.set(str);
-    return TP_SUCCESS;
+    return NumberToString<NoGC>(slice, d);
 }
 
-ParallelResult
+bool
 jit::StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out)
 {
-    return StringToNumber(slice, str, out) ? TP_SUCCESS : TP_FATAL;
+    return StringToNumber(slice, str, out);
 }
 
 #define PAR_RELATIONAL_OP(OP, EXPECTED)                                         \
 do {                                                                            \
     /* Optimize for two int-tagged operands (typical loop control). */          \
     if (lhs.isInt32() && rhs.isInt32()) {                                       \
         *res = (lhs.toInt32() OP rhs.toInt32()) == EXPECTED;                    \
     } else if (lhs.isNumber() && rhs.isNumber()) {                              \
@@ -256,238 +238,234 @@ do {                                    
         double r = rhs.toNumber();                                              \
         *res = (l OP r) == EXPECTED;                                            \
     } else if (lhs.isNumber() && rhs.isBoolean()) {                             \
         double l = lhs.toNumber();                                              \
         bool r = rhs.toBoolean();                                               \
         *res = (l OP r) == EXPECTED;                                            \
     } else {                                                                    \
         int32_t vsZero;                                                         \
-        ParallelResult ret = CompareMaybeStringsPar(slice, lhs, rhs, &vsZero);  \
-        if (ret != TP_SUCCESS)                                                  \
-            return ret;                                                         \
+        if (!CompareMaybeStringsPar(slice, lhs, rhs, &vsZero))                  \
+            return false;                                                       \
         *res = (vsZero OP 0) == EXPECTED;                                       \
     }                                                                           \
-    return TP_SUCCESS;                                                          \
+    return true;                                                                \
 } while(0)
 
-static ParallelResult
+static bool
 CompareStringsPar(ForkJoinSlice *slice, JSString *left, JSString *right, int32_t *res)
 {
     ScopedThreadSafeStringInspector leftInspector(left);
     ScopedThreadSafeStringInspector rightInspector(right);
     if (!leftInspector.ensureChars(slice) || !rightInspector.ensureChars(slice))
-        return TP_FATAL;
+        return false;
 
-    if (!CompareChars(leftInspector.chars(), left->length(),
-                      rightInspector.chars(), right->length(),
-                      res))
-        return TP_FATAL;
-
-    return TP_SUCCESS;
+    return CompareChars(leftInspector.chars(), left->length(),
+                        rightInspector.chars(), right->length(),
+                        res);
 }
 
-static ParallelResult
+static bool
 CompareMaybeStringsPar(ForkJoinSlice *slice, HandleValue v1, HandleValue v2, int32_t *res)
 {
     if (!v1.isString())
-        return TP_RETRY_SEQUENTIALLY;
+        return false;
     if (!v2.isString())
-        return TP_RETRY_SEQUENTIALLY;
+        return false;
     return CompareStringsPar(slice, v1.toString(), v2.toString(), res);
 }
 
 template<bool Equal>
-ParallelResult
+bool
 LooselyEqualImplPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     PAR_RELATIONAL_OP(==, Equal);
 }
 
-ParallelResult
+bool
 js::jit::LooselyEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     return LooselyEqualImplPar<true>(slice, lhs, rhs, res);
 }
 
-ParallelResult
+bool
 js::jit::LooselyUnequalPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     return LooselyEqualImplPar<false>(slice, lhs, rhs, res);
 }
 
 template<bool Equal>
-ParallelResult
+bool
 StrictlyEqualImplPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     if (lhs.isNumber()) {
         if (rhs.isNumber()) {
             *res = (lhs.toNumber() == rhs.toNumber()) == Equal;
-            return TP_SUCCESS;
+            return true;
         }
     } else if (lhs.isBoolean()) {
         if (rhs.isBoolean()) {
             *res = (lhs.toBoolean() == rhs.toBoolean()) == Equal;
-            return TP_SUCCESS;
+            return true;
         }
     } else if (lhs.isNull()) {
         if (rhs.isNull()) {
             *res = Equal;
-            return TP_SUCCESS;
+            return true;
         }
     } else if (lhs.isUndefined()) {
         if (rhs.isUndefined()) {
             *res = Equal;
-            return TP_SUCCESS;
+            return true;
         }
     } else if (lhs.isObject()) {
         if (rhs.isObject()) {
             *res = (lhs.toObjectOrNull() == rhs.toObjectOrNull()) == Equal;
-            return TP_SUCCESS;
+            return true;
         }
     } else if (lhs.isString()) {
         if (rhs.isString())
             return LooselyEqualImplPar<Equal>(slice, lhs, rhs, res);
     }
 
     *res = false;
-    return TP_SUCCESS;
+    return true;
 }
 
-ParallelResult
+bool
 js::jit::StrictlyEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     return StrictlyEqualImplPar<true>(slice, lhs, rhs, res);
 }
 
-ParallelResult
+bool
 js::jit::StrictlyUnequalPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     return StrictlyEqualImplPar<false>(slice, lhs, rhs, res);
 }
 
-ParallelResult
+bool
 js::jit::LessThanPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     PAR_RELATIONAL_OP(<, true);
 }
 
-ParallelResult
+bool
 js::jit::LessThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     PAR_RELATIONAL_OP(<=, true);
 }
 
-ParallelResult
+bool
 js::jit::GreaterThanPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     PAR_RELATIONAL_OP(>, true);
 }
 
-ParallelResult
+bool
 js::jit::GreaterThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     PAR_RELATIONAL_OP(>=, true);
 }
 
 template<bool Equal>
-ParallelResult
+bool
 StringsEqualImplPar(ForkJoinSlice *slice, HandleString lhs, HandleString rhs, bool *res)
 {
     int32_t vsZero;
-    ParallelResult ret = CompareStringsPar(slice, lhs, rhs, &vsZero);
-    if (ret != TP_SUCCESS)
+    bool ret = CompareStringsPar(slice, lhs, rhs, &vsZero);
+    if (ret != true)
         return ret;
     *res = (vsZero == 0) == Equal;
-    return TP_SUCCESS;
+    return true;
 }
 
-ParallelResult
+bool
 js::jit::StringsEqualPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *res)
 {
     return StringsEqualImplPar<true>(slice, v1, v2, res);
 }
 
-ParallelResult
+bool
 js::jit::StringsUnequalPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *res)
 {
     return StringsEqualImplPar<false>(slice, v1, v2, res);
 }
 
-ParallelResult
+bool
 jit::BitNotPar(ForkJoinSlice *slice, HandleValue in, int32_t *out)
 {
     if (in.isObject())
-        return TP_RETRY_SEQUENTIALLY;
+        return false;
     int i;
     if (!NonObjectToInt32(slice, in, &i))
-        return TP_FATAL;
+        return false;
     *out = ~i;
-    return TP_SUCCESS;
+    return true;
 }
 
 #define BIT_OP(OP)                                                      \
     JS_BEGIN_MACRO                                                      \
     int32_t left, right;                                                \
     if (lhs.isObject() || rhs.isObject())                               \
         return TP_RETRY_SEQUENTIALLY;                                   \
     if (!NonObjectToInt32(slice, lhs, &left) ||                         \
         !NonObjectToInt32(slice, rhs, &right))                          \
     {                                                                   \
-        return TP_FATAL;                                                \
+        return false;                                                   \
     }                                                                   \
     *out = (OP);                                                        \
-    return TP_SUCCESS;                                                  \
+    return true;                                                        \
     JS_END_MACRO
 
-ParallelResult
+bool
 jit::BitXorPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out)
 {
     BIT_OP(left ^ right);
 }
 
-ParallelResult
+bool
 jit::BitOrPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out)
 {
     BIT_OP(left | right);
 }
 
-ParallelResult
+bool
 jit::BitAndPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out)
 {
     BIT_OP(left & right);
 }
 
-ParallelResult
+bool
 jit::BitLshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out)
 {
     BIT_OP(left << (right & 31));
 }
 
-ParallelResult
+bool
 jit::BitRshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out)
 {
     BIT_OP(left >> (right & 31));
 }
 
 #undef BIT_OP
 
-ParallelResult
+bool
 jit::UrshValuesPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs,
                    Value *out)
 {
     uint32_t left;
     int32_t right;
     if (lhs.isObject() || rhs.isObject())
-        return TP_RETRY_SEQUENTIALLY;
+        return false;
     if (!NonObjectToUint32(slice, lhs, &left) || !NonObjectToInt32(slice, rhs, &right))
-        return TP_FATAL;
+        return false;
     left >>= right & 31;
     out->setNumber(uint32_t(left));
-    return TP_SUCCESS;
+    return true;
 }
 
 void
 jit::AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
               jsbytecode *bytecode)
 {
     // Spew before asserts to help with diagnosing failures.
     Spew(SpewBailouts,
@@ -501,18 +479,17 @@ jit::AbortPar(ParallelBailoutCause cause
     JS_ASSERT(InParallelSection());
     JS_ASSERT(outermostScript != nullptr);
     JS_ASSERT(currentScript != nullptr);
     JS_ASSERT(outermostScript->hasParallelIonScript());
 
     ForkJoinSlice *slice = ForkJoinSlice::Current();
 
     JS_ASSERT(slice->bailoutRecord->depth == 0);
-    slice->bailoutRecord->setCause(cause, outermostScript,
-                                   currentScript, bytecode);
+    slice->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode);
 }
 
 void
 jit::PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript)
 {
     Spew(SpewBailouts,
          "Propagate parallel abort via %p:%s:%d (%p:%s:%d)",
          outermostScript, outermostScript->filename(), outermostScript->lineno,
@@ -567,33 +544,31 @@ jit::CallToUncompiledScriptPar(JSObject 
         }
     } else {
         JS_ASSERT(func->isNative());
         Spew(SpewBailouts, "Call to native function");
     }
 #endif
 }
 
-ParallelResult
+JSObject *
 jit::InitRestParameterPar(ForkJoinSlice *slice, uint32_t length, Value *rest,
-                          HandleObject templateObj, HandleObject res,
-                          MutableHandleObject out)
+                          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
     // path, but duplicating the initGCThing logic is too tedious.
     JS_ASSERT(res);
     JS_ASSERT(res->is<ArrayObject>());
     JS_ASSERT(!res->getDenseInitializedLength());
     JS_ASSERT(res->type() == templateObj->type());
 
     if (length > 0) {
         JSObject::EnsureDenseResult edr =
             res->ensureDenseElementsPreservePackedFlag(slice, 0, length);
         if (edr != JSObject::ED_OK)
-            return TP_FATAL;
+            return nullptr;
         res->initDenseElements(0, rest, length);
         res->as<ArrayObject>().setLengthInt32(length);
     }
 
-    out.set(res);
-    return TP_SUCCESS;
+    return res;
 }
--- a/js/src/jit/ParallelFunctions.h
+++ b/js/src/jit/ParallelFunctions.h
@@ -20,57 +20,55 @@ bool CheckOverRecursedPar(ForkJoinSlice 
 bool CheckInterruptPar(ForkJoinSlice *slice);
 
 // Extends the given array with `length` new holes.  Returns nullptr on
 // failure or else `array`, which is convenient during code
 // generation.
 JSObject *ExtendArrayPar(ForkJoinSlice *slice, JSObject *array, uint32_t length);
 
 // Set properties and elements on thread local objects.
-ParallelResult SetPropertyPar(ForkJoinSlice *slice, HandleObject obj, HandlePropertyName name,
-                              HandleValue value, bool strict, jsbytecode *pc);
-ParallelResult SetElementPar(ForkJoinSlice *slice, HandleObject obj, HandleValue index,
-                             HandleValue value, bool strict);
+bool SetPropertyPar(ForkJoinSlice *slice, HandleObject obj, HandlePropertyName name,
+                    HandleValue value, bool strict, jsbytecode *pc);
+bool SetElementPar(ForkJoinSlice *slice, HandleObject obj, HandleValue index,
+                   HandleValue value, bool strict);
 
 // String related parallel functions. These tend to call existing VM functions
 // that take a ThreadSafeContext.
-ParallelResult ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right,
-                                MutableHandleString out);
-ParallelResult IntToStringPar(ForkJoinSlice *slice, int i, MutableHandleString out);
-ParallelResult DoubleToStringPar(ForkJoinSlice *slice, double d, MutableHandleString out);
-ParallelResult StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out);
+JSString *ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right);
+JSFlatString *IntToStringPar(ForkJoinSlice *slice, int i);
+JSString *DoubleToStringPar(ForkJoinSlice *slice, double d);
+bool StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out);
 
 // Binary and unary operator functions on values. These tend to return
 // RETRY_SEQUENTIALLY if the values are objects.
-ParallelResult StrictlyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-ParallelResult StrictlyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-ParallelResult LooselyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-ParallelResult LooselyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-ParallelResult LessThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-ParallelResult LessThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-ParallelResult GreaterThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
-ParallelResult GreaterThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool StrictlyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool StrictlyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool LooselyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool LooselyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool LessThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool LessThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool GreaterThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
+bool GreaterThanOrEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
 
-ParallelResult StringsEqualPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *);
-ParallelResult StringsUnequalPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *);
+bool StringsEqualPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *);
+bool StringsUnequalPar(ForkJoinSlice *slice, HandleString v1, HandleString v2, bool *);
 
-ParallelResult BitNotPar(ForkJoinSlice *slice, HandleValue in, int32_t *out);
-ParallelResult BitXorPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
-ParallelResult BitOrPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
-ParallelResult BitAndPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
-ParallelResult BitLshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
-ParallelResult BitRshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
+bool BitNotPar(ForkJoinSlice *slice, HandleValue in, int32_t *out);
+bool BitXorPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
+bool BitOrPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
+bool BitAndPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
+bool BitLshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
+bool BitRshPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs, int32_t *out);
 
-ParallelResult UrshValuesPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs,
+bool UrshValuesPar(ForkJoinSlice *slice, HandleValue lhs, HandleValue rhs,
                              Value *out);
 
 // Make a new rest parameter in parallel.
-ParallelResult InitRestParameterPar(ForkJoinSlice *slice, uint32_t length, Value *rest,
-                                    HandleObject templateObj, HandleObject res,
-                                    MutableHandleObject out);
+JSObject *InitRestParameterPar(ForkJoinSlice *slice, 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);
 
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -22,18 +22,17 @@ namespace jit {
 enum DataType {
     Type_Void,
     Type_Bool,
     Type_Int32,
     Type_Double,
     Type_Pointer,
     Type_Object,
     Type_Value,
-    Type_Handle,
-    Type_ParallelResult
+    Type_Handle
 };
 
 struct PopValues
 {
     uint32_t numValues;
 
     explicit PopValues(uint32_t numValues)
       : numValues(numValues)
@@ -215,20 +214,18 @@ struct VMFunction
         argumentRootTypes(argRootTypes),
         outParamRootType(outParamRootType),
         executionMode(executionMode),
         extraValuesToPop(extraValuesToPop)
     {
         // Check for valid failure/return type.
         JS_ASSERT_IF(outParam != Type_Void && executionMode == SequentialExecution,
                      returnType == Type_Bool);
-        JS_ASSERT_IF(executionMode == ParallelExecution, returnType == Type_ParallelResult);
         JS_ASSERT(returnType == Type_Bool ||
-                  returnType == Type_Object ||
-                  returnType == Type_ParallelResult);
+                  returnType == Type_Object);
     }
 
     VMFunction(const VMFunction &o) {
         init(o);
     }
 
     void init(const VMFunction &o) {
         JS_ASSERT(!wrapped);
@@ -275,17 +272,16 @@ template <> struct TypeToDataType<JSFlat
 template <> struct TypeToDataType<HandleObject> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleString> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandlePropertyName> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleFunction> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<Handle<StaticBlockObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleScript> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleValue> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<MutableHandleValue> { static const DataType result = Type_Handle; };
-template <> struct TypeToDataType<ParallelResult> { static const DataType result = Type_ParallelResult; };
 
 // Convert argument types to properties of the argument known by the jit.
 template <class T> struct TypeToArgProperties {
     static const uint32_t result =
         (sizeof(T) <= sizeof(void *) ? VMFunction::Word : VMFunction::Double);
 };
 template <> struct TypeToArgProperties<const Value &> {
     static const uint32_t result = TypeToArgProperties<Value>::result | VMFunction::ByRef;
@@ -390,18 +386,18 @@ template <> struct MatchContext<JSContex
 template <> struct MatchContext<ExclusiveContext *> {
     static const ExecutionMode execMode = SequentialExecution;
 };
 template <> struct MatchContext<ForkJoinSlice *> {
     static const ExecutionMode execMode = ParallelExecution;
 };
 template <> struct MatchContext<ThreadSafeContext *> {
     // ThreadSafeContext functions can be called from either mode, but for
-    // calling from parallel they need to be wrapped first to return a
-    // ParallelResult, so we default to SequentialExecution here.
+    // calling from parallel they should be wrapped first, so we default to
+    // SequentialExecution here.
     static const ExecutionMode execMode = SequentialExecution;
 };
 
 #define FOR_EACH_ARGS_1(Macro, Sep, Last) Macro(1) Last(1)
 #define FOR_EACH_ARGS_2(Macro, Sep, Last) FOR_EACH_ARGS_1(Macro, Sep, Sep) Macro(2) Last(2)
 #define FOR_EACH_ARGS_3(Macro, Sep, Last) FOR_EACH_ARGS_2(Macro, Sep, Sep) Macro(3) Last(3)
 #define FOR_EACH_ARGS_4(Macro, Sep, Last) FOR_EACH_ARGS_3(Macro, Sep, Sep) Macro(4) Last(4)
 #define FOR_EACH_ARGS_5(Macro, Sep, Last) FOR_EACH_ARGS_4(Macro, Sep, Sep) Macro(5) Last(5)
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -766,19 +766,16 @@ IonRuntime::generateVMWrapper(JSContext 
 
     // Test for failure.
     switch (f.failType()) {
       case Type_Object:
       case Type_Bool:
         // Called functions return bools, which are 0/false and non-zero/true
         masm.branch32(Assembler::Equal, r0, Imm32(0), masm.failureLabel(f.executionMode));
         break;
-      case Type_ParallelResult:
-        masm.branch32(Assembler::NotEqual, r0, Imm32(TP_SUCCESS), masm.failureLabel(f.executionMode));
-        break;
       default:
         MOZ_ASSUME_UNREACHABLE("unknown failure kind");
     }
 
     // Load the outparam and free any allocated stack.
     switch (f.outParam) {
       case Type_Handle:
         masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand);
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -620,20 +620,16 @@ IonRuntime::generateVMWrapper(JSContext 
     switch (f.failType()) {
       case Type_Object:
         masm.branchTestPtr(Assembler::Zero, rax, rax, masm.failureLabel(f.executionMode));
         break;
       case Type_Bool:
         masm.testb(rax, rax);
         masm.j(Assembler::Zero, masm.failureLabel(f.executionMode));
         break;
-      case Type_ParallelResult:
-        masm.branchPtr(Assembler::NotEqual, rax, Imm32(TP_SUCCESS),
-                       masm.failureLabel(f.executionMode));
-        break;
       default:
         MOZ_ASSUME_UNREACHABLE("unknown failure kind");
     }
 
     // Load the outparam and free any allocated stack.
     switch (f.outParam) {
       case Type_Handle:
         masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand);
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -654,20 +654,16 @@ IonRuntime::generateVMWrapper(JSContext 
     switch (f.failType()) {
       case Type_Object:
         masm.branchTestPtr(Assembler::Zero, eax, eax, masm.failureLabel(f.executionMode));
         break;
       case Type_Bool:
         masm.testb(eax, eax);
         masm.j(Assembler::Zero, masm.failureLabel(f.executionMode));
         break;
-      case Type_ParallelResult:
-        masm.branchPtr(Assembler::NotEqual, eax, Imm32(TP_SUCCESS),
-                       masm.failureLabel(f.executionMode));
-        break;
       default:
         MOZ_ASSUME_UNREACHABLE("unknown failure kind");
     }
 
     // Load the outparam and free any allocated stack.
     switch (f.outParam) {
       case Type_Handle:
         masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -754,27 +754,30 @@ js::WouldDefinePastNonwritableLength(Thr
 
     if (arr->lengthIsWritable()) {
         *definesPast = false;
         return true;
     }
 
     *definesPast = true;
 
+    // Error in strict mode code or warn with strict option.
+    unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING);
+    if (cx->isForkJoinSlice())
+        return cx->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, flags);
+
     if (!cx->isJSContext())
         return true;
 
     JSContext *ncx = cx->asJSContext();
 
     if (!strict && !ncx->hasExtraWarningsOption())
         return true;
 
-    // Error in strict mode code or warn with strict option.
     // XXX include the index and maybe array length in the error message
-    unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING);
     return JS_ReportErrorFlagsAndNumber(ncx, flags, js_GetErrorMessage, nullptr,
                                         JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
 }
 
 static bool
 array_addProperty(JSContext *cx, HandleObject obj, HandleId id,
                   MutableHandleValue vp)
 {
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -344,16 +344,21 @@ PopulateReportBlame(JSContext *cx, JSErr
  * error reporter directly.
  *
  * Furthermore, callers of js_ReportOutOfMemory (viz., malloc) assume a GC does
  * not occur, so GC must be avoided or suppressed.
  */
 void
 js_ReportOutOfMemory(ThreadSafeContext *cxArg)
 {
+    if (cxArg->isForkJoinSlice()) {
+        cxArg->asForkJoinSlice()->setPendingAbortFatal(ParallelBailoutOutOfMemory);
+        return;
+    }
+
     if (!cxArg->isJSContext())
         return;
     JSContext *cx = cxArg->asJSContext();
 
     cx->runtime()->hadOutOfMemory = true;
 
     if (JS_IsRunning(cx)) {
         cx->setPendingException(StringValue(cx->names().outOfMemory));
@@ -413,17 +418,25 @@ void
 js_ReportOverRecursed(ThreadSafeContext *cx)
 {
     js_ReportOverRecursed(cx->maybeJSContext());
 }
 
 void
 js_ReportAllocationOverflow(ThreadSafeContext *cxArg)
 {
-    if (!cxArg || !cxArg->isJSContext())
+    if (!cxArg)
+        return;
+
+    if (cxArg->isForkJoinSlice()) {
+        cxArg->asForkJoinSlice()->setPendingAbortFatal(ParallelBailoutOutOfMemory);
+        return;
+    }
+
+    if (!cxArg->isJSContext())
         return;
     JSContext *cx = cxArg->asJSContext();
 
     AutoSuppressGC suppressGC(cx);
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW);
 }
 
 /*
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -552,20 +552,20 @@ JSContext::currentScript(jsbytecode **pp
 template <JSThreadSafeNative threadSafeNative>
 inline bool
 JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     return threadSafeNative(cx, argc, vp);
 }
 
 template <JSThreadSafeNative threadSafeNative>
-inline js::ParallelResult
+inline bool
 JSParallelNativeThreadSafeWrapper(js::ForkJoinSlice *slice, unsigned argc, JS::Value *vp)
 {
-    return threadSafeNative(slice, argc, vp) ? js::TP_SUCCESS : js::TP_FATAL;
+    return threadSafeNative(slice, argc, vp);
 }
 
 /* static */ inline JSContext *
 js::ExecutionModeTraits<js::SequentialExecution>::toContextType(ExclusiveContext *cx)
 {
     return cx->asJSContext();
 }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3497,25 +3497,18 @@ DefinePropertyOrElement(typename Executi
         setter == JS_StrictPropertyStub &&
         attrs == JSPROP_ENUMERATE &&
         (!obj->isIndexed() || !obj->nativeContainsPure(id)))
     {
         uint32_t index = JSID_TO_INT(id);
         bool definesPast;
         if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast))
             return false;
-        if (definesPast) {
-            /*
-             * Strict mode changes semantics and we must throw, so bail out of
-             * parallel execution if we are strict.
-             */
-            if (mode == ParallelExecution)
-                return !setterIsStrict;
+        if (definesPast)
             return true;
-        }
 
         JSObject::EnsureDenseResult result;
         if (mode == ParallelExecution) {
             if (obj->writeToIndexWouldMarkNotPacked(index))
                 return false;
             result = obj->ensureDenseElementsPreservePackedFlag(cx, index, 1);
         } else {
             result = obj->ensureDenseElements(cx->asExclusiveContext(), index, 1);
@@ -3538,22 +3531,18 @@ DefinePropertyOrElement(typename Executi
                                         attrs, value, setterIsStrict);
         }
 
         uint32_t index;
         if (js_IdIsIndex(id, &index)) {
             bool definesPast;
             if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast))
                 return false;
-            if (definesPast) {
-                /* Bail out of parallel execution if we are strict to throw. */
-                if (mode == ParallelExecution)
-                    return !setterIsStrict;
+            if (definesPast)
                 return true;
-            }
         }
     }
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     RootedShape shape(cx, JSObject::putProperty<mode>(cx, obj, id, getter, setter,
                                                       SHAPE_INVALID_SLOT,
                                                       attrs, flags, shortid));
@@ -4581,36 +4570,57 @@ js::ReportIfUndeclaredVarAssignment(JSCo
            JS_ReportErrorFlagsAndNumber(cx,
                                         JSREPORT_WARNING | JSREPORT_STRICT |
                                         JSREPORT_STRICT_MODE_ERROR,
                                         js_GetErrorMessage, nullptr,
                                         JSMSG_UNDECLARED_VAR, bytes.ptr());
 }
 
 bool
-JSObject::reportReadOnly(JSContext *cx, jsid id, unsigned report)
+JSObject::reportReadOnly(ThreadSafeContext *cxArg, jsid id, unsigned report)
 {
+    if (cxArg->isForkJoinSlice())
+        return cxArg->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, report);
+
+    if (!cxArg->isJSContext())
+        return true;
+
+    JSContext *cx = cxArg->asJSContext();
     RootedValue val(cx, IdToValue(id));
     return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
                                     JSDVG_IGNORE_STACK, val, NullPtr(),
                                     nullptr, nullptr);
 }
 
 bool
-JSObject::reportNotConfigurable(JSContext *cx, jsid id, unsigned report)
+JSObject::reportNotConfigurable(ThreadSafeContext *cxArg, jsid id, unsigned report)
 {
+    if (cxArg->isForkJoinSlice())
+        return cxArg->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, report);
+
+    if (!cxArg->isJSContext())
+        return true;
+
+    JSContext *cx = cxArg->asJSContext();
     RootedValue val(cx, IdToValue(id));
     return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
                                     JSDVG_IGNORE_STACK, val, NullPtr(),
                                     nullptr, nullptr);
 }
 
 bool
-JSObject::reportNotExtensible(JSContext *cx, unsigned report)
+JSObject::reportNotExtensible(ThreadSafeContext *cxArg, unsigned report)
 {
+    if (cxArg->isForkJoinSlice())
+        return cxArg->asForkJoinSlice()->reportError(ParallelBailoutUnsupportedVM, report);
+
+    if (!cxArg->isJSContext())
+        return true;
+
+    JSContext *cx = cxArg->asJSContext();
     RootedValue val(cx, ObjectValue(*this));
     return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
                                     JSDVG_IGNORE_STACK, val, NullPtr(),
                                     nullptr, nullptr);
 }
 
 bool
 JSObject::callMethod(JSContext *cx, HandleId id, unsigned argc, Value *argv, MutableHandleValue vp)
@@ -4822,26 +4832,21 @@ baseops::SetPropertyHelper(typename Exec
                 return false;
             extensible = obj->nonProxyIsExtensible();
         } else {
             if (!JSObject::isExtensible(cxArg->asJSContext(), obj, &extensible))
                 return false;
         }
 
         if (!extensible) {
-            /* Bail out of parallel execution if we are strict to throw. */
-            if (mode == ParallelExecution)
-                return !strict;
-
             /* Error in strict mode code, warn with extra warnings option, otherwise do nothing. */
-            JSContext *cx = cxArg->asJSContext();
             if (strict)
-                return obj->reportNotExtensible(cx);
-            if (cx->hasExtraWarningsOption())
-                return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
+                return obj->reportNotExtensible(cxArg);
+            if (mode == SequentialExecution && cxArg->asJSContext()->hasExtraWarningsOption())
+                return obj->reportNotExtensible(cxArg, JSREPORT_STRICT | JSREPORT_WARNING);
             return true;
         }
 
         if (mode == ParallelExecution) {
             if (obj->isDelegate())
                 return false;
 
             if (getter != JS_PropertyStub || !HasTypePropertyId(obj, id, vp))
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -802,19 +802,19 @@ class JSObject : public js::ObjectImpl
      * FIXME: bug 593129 -- slot allocation should be done by object methods
      * after calling object-parameter-free shape methods, avoiding coupling
      * logic across the object vs. shape module wall.
      */
     static bool allocSlot(js::ThreadSafeContext *cx, JS::HandleObject obj, uint32_t *slotp);
     void freeSlot(uint32_t slot);
 
   public:
-    static bool reportReadOnly(JSContext *cx, jsid id, unsigned report = JSREPORT_ERROR);
-    bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR);
-    bool reportNotExtensible(JSContext *cx, unsigned report = JSREPORT_ERROR);
+    static bool reportReadOnly(js::ThreadSafeContext *cx, jsid id, unsigned report = JSREPORT_ERROR);
+    bool reportNotConfigurable(js::ThreadSafeContext *cx, jsid id, unsigned report = JSREPORT_ERROR);
+    bool reportNotExtensible(js::ThreadSafeContext *cx, unsigned report = JSREPORT_ERROR);
 
     /*
      * Get the property with the given id, then call it as a function with the
      * given arguments, providing this object as |this|. If the property isn't
      * callable a TypeError will be thrown. On success the value returned by
      * the call is stored in *vp.
      */
     bool callMethod(JSContext *cx, js::HandleId id, unsigned argc, js::Value *argv,
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -95,26 +95,42 @@ ForkJoinSlice::requestGC(JS::gcreason::R
 }
 
 void
 ForkJoinSlice::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
 {
     MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
 }
 
+bool
+ForkJoinSlice::setPendingAbortFatal(ParallelBailoutCause cause)
+{
+    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
+    return false;
+}
+
 void
 ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
                                 JSScript *outermostScript,
                                 JSScript *currentScript,
                                 jsbytecode *currentPc)
 {
     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)
 {
     MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
 }
 
 bool
 js::InExclusiveParallelSection()
@@ -383,16 +399,19 @@ class ForkJoinShared : public TaskExecut
 
     // Requests a GC, either full or specific to a zone.
     void requestGC(JS::gcreason::Reason reason);
     void requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason);
 
     // Requests that computation abort.
     void setAbortFlag(bool fatal);
 
+    // Set the fatal flag for the next abort.
+    void setPendingAbortFatal() { fatal_ = true; }
+
     JSRuntime *runtime() { return cx_->runtime(); }
     JS::Zone *zone() { return cx_->zone(); }
 
     JSContext *acquireContext() { PR_Lock(cxLock_); return cx_; }
     void releaseContext() { PR_Unlock(cxLock_); }
 };
 
 class AutoEnterWarmup
@@ -970,37 +989,37 @@ BailoutExplanation(ParallelBailoutCause 
         return "no particular reason";
       case ParallelBailoutCompilationSkipped:
         return "compilation failed (method skipped)";
       case ParallelBailoutCompilationFailure:
         return "compilation failed";
       case ParallelBailoutInterrupt:
         return "interrupted";
       case ParallelBailoutFailedIC:
-        return "at runtime, the behavior changed, invalidating compiled code (IC update)";
+        return "failed to attach stub to IC";
       case ParallelBailoutHeapBusy:
         return "heap busy flag set during interrupt";
       case ParallelBailoutMainScriptNotPresent:
         return "main script not present";
       case ParallelBailoutCalledToUncompiledScript:
         return "called to uncompiled script";
       case ParallelBailoutIllegalWrite:
         return "illegal write";
       case ParallelBailoutAccessToIntrinsic:
         return "access to intrinsic";
       case ParallelBailoutOverRecursed:
         return "over recursed";
       case ParallelBailoutOutOfMemory:
         return "out of memory";
       case ParallelBailoutUnsupported:
         return "unsupported";
+      case ParallelBailoutUnsupportedVM:
+        return "unsupported operation in VM call";
       case ParallelBailoutUnsupportedStringComparison:
         return "unsupported string comparison";
-      case ParallelBailoutUnsupportedSparseArray:
-        return "unsupported sparse array";
       case ParallelBailoutRequestedGC:
         return "requested GC";
       case ParallelBailoutRequestedZoneGC:
         return "requested zone GC";
       default:
         return "no known reason";
     }
 }
@@ -1433,18 +1452,17 @@ ForkJoinShared::executePortion(PerThread
     JS_ASSERT(fun->is<JSFunction>());
     RootedFunction callee(perThread, &fun->as<JSFunction>());
     if (!callee->nonLazyScript()->hasParallelIonScript()) {
         // Sometimes, particularly with GCZeal, the parallel ion
         // script can be collected between starting the parallel
         // op and reaching this point.  In that case, we just fail
         // and fallback.
         Spew(SpewOps, "Down (Script no longer present)");
-        slice.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent,
-                                      nullptr, nullptr, nullptr);
+        slice.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent);
         setAbortFlag(false);
     } else {
         ParallelIonInvoke<3> fii(cx_->runtime(), callee, 3);
 
         fii.args[0] = Int32Value(slice.sliceId);
         fii.args[1] = Int32Value(slice.numSlices);
         fii.args[2] = BooleanValue(false);
 
@@ -1474,18 +1492,17 @@ ForkJoinShared::check(ForkJoinSlice &sli
             // requestZoneGC() methods should be invoked.
             JS_ASSERT(!cx_->runtime()->gcIsNeeded);
 
             // If interrupt is requested, bring worker threads to a halt,
             // service the interrupt, then let them start back up again.
             // AutoRendezvous autoRendezvous(slice);
             // if (!js_HandleExecutionInterrupt(cx_))
             //     return setAbortFlag(true);
-            slice.bailoutRecord->setCause(ParallelBailoutInterrupt,
-                                          nullptr, nullptr, nullptr);
+            slice.bailoutRecord->setCause(ParallelBailoutInterrupt);
             setAbortFlag(false);
             return false;
         }
     } else if (rendezvous_) {
         joinRendezvous(slice);
     }
 
     return true;
@@ -1692,30 +1709,36 @@ ForkJoinSlice::InitializeTLS()
     }
     return true;
 }
 
 void
 ForkJoinSlice::requestGC(JS::gcreason::Reason reason)
 {
     shared->requestGC(reason);
-    bailoutRecord->setCause(ParallelBailoutRequestedGC,
-                            nullptr, nullptr, nullptr);
+    bailoutRecord->setCause(ParallelBailoutRequestedGC);
     shared->setAbortFlag(false);
 }
 
 void
 ForkJoinSlice::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
 {
     shared->requestZoneGC(zone, reason);
-    bailoutRecord->setCause(ParallelBailoutRequestedZoneGC,
-                            nullptr, nullptr, nullptr);
+    bailoutRecord->setCause(ParallelBailoutRequestedZoneGC);
     shared->setAbortFlag(false);
 }
 
+bool
+ForkJoinSlice::setPendingAbortFatal(ParallelBailoutCause cause)
+{
+    shared->setPendingAbortFatal();
+    bailoutRecord->setCause(cause);
+    return false;
+}
+
 /////////////////////////////////////////////////////////////////////////////
 
 uint32_t
 js::ForkJoinSlices(JSContext *cx)
 {
     // Parallel workers plus this main thread.
     return cx->runtime()->threadPool.numWorkers() + 1;
 }
@@ -1738,30 +1761,39 @@ js::ParallelBailoutRecord::reset(JSConte
 }
 
 void
 js::ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
                                     JSScript *outermostScript,
                                     JSScript *currentScript,
                                     jsbytecode *currentPc)
 {
+    this->cause = cause;
+    updateCause(cause, outermostScript, currentScript, currentPc);
+}
+
+void
+js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
+                                       JSScript *outermostScript,
+                                       JSScript *currentScript,
+                                       jsbytecode *currentPc)
+{
     JS_ASSERT_IF(outermostScript, currentScript);
     JS_ASSERT_IF(outermostScript, outermostScript->hasParallelIonScript());
     JS_ASSERT_IF(currentScript, outermostScript);
     JS_ASSERT_IF(!currentScript, !currentPc);
 
-    this->cause = cause;
+    if (this->cause == ParallelBailoutNone)
+        this->cause = cause;
 
-    if (outermostScript) {
+    if (outermostScript)
         this->topScript = outermostScript;
-    }
 
-    if (currentScript) {
+    if (currentScript)
         addTrace(currentScript, currentPc);
-    }
 }
 
 void
 js::ParallelBailoutRecord::addTrace(JSScript *script,
                                     jsbytecode *pc)
 {
     // Ideally, this should never occur, because we should always have
     // a script when we invoke setCause, but I havent' fully
--- a/js/src/vm/ForkJoin.h
+++ b/js/src/vm/ForkJoin.h
@@ -218,41 +218,41 @@ struct IonLIRTraceData {
 };
 
 ///////////////////////////////////////////////////////////////////////////
 // Bailout tracking
 
 enum ParallelBailoutCause {
     ParallelBailoutNone,
 
-    // compiler returned Method_Skipped
+    // Compiler returned Method_Skipped
     ParallelBailoutCompilationSkipped,
 
-    // compiler returned Method_CantCompile
+    // Compiler returned Method_CantCompile
     ParallelBailoutCompilationFailure,
 
-    // the periodic interrupt failed, which can mean that either
+    // The periodic interrupt failed, which can mean that either
     // another thread canceled, the user interrupted us, etc
     ParallelBailoutInterrupt,
 
-    // an IC update failed
+    // An IC update failed
     ParallelBailoutFailedIC,
 
     // Heap busy flag was set during interrupt
     ParallelBailoutHeapBusy,
 
     ParallelBailoutMainScriptNotPresent,
     ParallelBailoutCalledToUncompiledScript,
     ParallelBailoutIllegalWrite,
     ParallelBailoutAccessToIntrinsic,
     ParallelBailoutOverRecursed,
     ParallelBailoutOutOfMemory,
     ParallelBailoutUnsupported,
+    ParallelBailoutUnsupportedVM,
     ParallelBailoutUnsupportedStringComparison,
-    ParallelBailoutUnsupportedSparseArray,
     ParallelBailoutRequestedGC,
     ParallelBailoutRequestedZoneGC,
 };
 
 struct ParallelBailoutTrace {
     JSScript *script;
     jsbytecode *bytecode;
 };
@@ -266,19 +266,23 @@ struct ParallelBailoutRecord {
     // but for now we gather at most a single frame.
     static const uint32_t MaxDepth = 1;
     uint32_t depth;
     ParallelBailoutTrace trace[MaxDepth];
 
     void init(JSContext *cx);
     void reset(JSContext *cx);
     void setCause(ParallelBailoutCause cause,
-                  JSScript *outermostScript,   // inliner (if applicable)
-                  JSScript *currentScript,     // inlinee (if applicable)
-                  jsbytecode *currentPc);
+                  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);
 };
 
 struct ForkJoinShared;
 
 class ForkJoinSlice : public ThreadSafeContext
 {
@@ -310,16 +314,28 @@ class ForkJoinSlice : public ThreadSafeC
     // |TriggerCompartmentGC()| as appropriate once the parallel
     // section is complete. This is done because those routines do
     // various preparations that are not thread-safe, and because the
     // full set of arenas is not available until the end of the
     // parallel section.
     void requestGC(JS::gcreason::Reason reason);
     void requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason);
 
+    // Set the fatal flag for the next abort. Used to distinguish retry or
+    // fatal aborts from VM functions.
+    bool setPendingAbortFatal(ParallelBailoutCause cause);
+
+    // Reports an unsupported operation, returning false if we are reporting
+    // an error. Otherwise drop the warning on the floor.
+    bool reportError(ParallelBailoutCause cause, unsigned report) {
+        if (report & JSREPORT_ERROR)
+            return setPendingAbortFatal(cause);
+        return true;
+    }
+
     // During the parallel phase, this method should be invoked
     // periodically, for example on every backedge, similar to the
     // interrupt check.  If it returns false, then the parallel phase
     // has been aborted and so you should bailout.  The function may
     // also rendesvous to perform GC or do other similar things.
     //
     // This function is guaranteed to have no effect if both
     // runtime()->interrupt is zero.  Ion-generated code takes