Backed out 7 changesets (bug 1062869) for ASAN UAF and Hazard Analysis failures.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 18 Sep 2014 14:03:31 -0400
changeset 206020 33d4eb54177307c3a07857032dca982879400774
parent 206019 18be3ab7b25e22e36fa4ab00594d394ebb35427f
child 206021 fc35048535c813bd262c0267b5d15c177bbff80b
push id49332
push userryanvm@gmail.com
push dateThu, 18 Sep 2014 18:03:15 +0000
treeherdermozilla-inbound@33d4eb541773 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1062869
milestone35.0a1
backs outf62b115f169f6b3066bd80fc9192def682b03c10
48943bce060d18d3b56e8e41fb54667aba97cdcb
846d57ff7684bc8c3c29f91b1cec1119dcb03ee3
85b9beefb942c6ae23f1e92686137dbc78a02fa4
a98f73f954eee9071d8701e215b85df93d78e273
b111868be30f062c44f0ab836b33d523e2a6ec90
f3397cf24e64e0eae0c1d31d38bd7575b3e3a884
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
Backed out 7 changesets (bug 1062869) for ASAN UAF and Hazard Analysis failures. Backed out changeset f62b115f169f (bug 1062869) Backed out changeset 48943bce060d (bug 1062869) Backed out changeset 846d57ff7684 (bug 1062869) Backed out changeset 85b9beefb942 (bug 1062869) Backed out changeset a98f73f954ee (bug 1062869) Backed out changeset b111868be30f (bug 1062869) Backed out changeset f3397cf24e64 (bug 1062869) CLOSED TREE
js/src/jit/BaselineBailouts.cpp
js/src/jit/CompileInfo.h
js/src/jit/Ion.cpp
js/src/jit/IonCode.h
js/src/jit/IonFrames.cpp
js/src/jit/JitFrameIterator.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/RematerializedFrame.cpp
js/src/jit/ScalarReplacement.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1338,21 +1338,20 @@ jit::BailoutIonToBaseline(JSContext *cx,
     iter.script()->updateBaselineOrIonRaw(cx);
 
     // Allocate buffer to hold stack replacement data.
     BaselineStackBuilder builder(iter, 1024);
     if (!builder.init())
         return BAILOUT_RETURN_FATAL_ERROR;
     JitSpew(JitSpew_BaselineBailouts, "  Incoming frame ptr = %p", builder.startFrame());
 
-    RInstructionResults instructionResults;
-    activation->maybeTakeIonFrameRecovery(iter.jsFrame(), &instructionResults);
+    AutoValueVector instructionResults(cx);
     SnapshotIterator snapIter(iter);
 
-    if (!snapIter.initInstructionResults(cx, &instructionResults))
+    if (!snapIter.initIntructionResults(instructionResults))
         return BAILOUT_RETURN_FATAL_ERROR;
 
 #ifdef TRACK_SNAPSHOTS
     snapIter.spewBailingFrom();
 #endif
 
     RootedFunction callee(cx, iter.maybeCallee());
     RootedScript scr(cx, iter.script());
@@ -1376,18 +1375,22 @@ jit::BailoutIonToBaseline(JSContext *cx,
     jsbytecode *callerPC = nullptr;
     RootedFunction fun(cx, callee);
     AutoValueVector startFrameFormals(cx);
 
     RootedScript topCaller(cx);
     jsbytecode *topCallerPC = nullptr;
 
     while (true) {
-        // Skip recover instructions as they are already recovered by |initInstructionResults|.
-        snapIter.settleOnFrame();
+        if (!snapIter.instruction()->isResumePoint()) {
+            if (!snapIter.instruction()->recover(cx, snapIter))
+                return BAILOUT_RETURN_FATAL_ERROR;
+            snapIter.nextInstruction();
+            continue;
+        }
 
         if (frameNo > 0) {
             TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));
             TraceLogStartEvent(logger, TraceLogger::Baseline);
         }
 
         JitSpew(JitSpew_BaselineBailouts, "    FrameNo %d", frameNo);
 
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -439,71 +439,41 @@ class CompileInfo
         return executionMode_ == ParallelExecution;
     }
 
     // Returns true if a slot can be observed out-side the current frame while
     // the frame is active on the stack.  This implies that these definitions
     // would have to be executed and that they cannot be removed even if they
     // are unused.
     bool isObservableSlot(uint32_t slot) const {
-        if (isObservableFrameSlot(slot))
-            return true;
-
-        if (isObservableArgumentSlot(slot))
-            return true;
-
-        return false;
-    }
-
-    bool isObservableFrameSlot(uint32_t slot) const {
         if (!funMaybeLazy())
             return false;
 
         // The |this| value must always be observable.
         if (slot == thisSlot())
             return true;
 
         // If the function may need an arguments object, then make sure to
         // preserve the scope chain, because it may be needed to construct the
         // arguments object during bailout. If we've already created an
         // arguments object (or got one via OSR), preserve that as well.
         if (hasArguments() && (slot == scopeChainSlot() || slot == argsObjSlot()))
             return true;
 
-        return false;
-    }
-
-    bool isObservableArgumentSlot(uint32_t slot) const {
-        if (!funMaybeLazy())
-            return false;
-
         // Function.arguments can be used to access all arguments in non-strict
         // scripts, so we can't optimize out any arguments.
         if ((hasArguments() || !script()->strict()) &&
             firstArgSlot() <= slot && slot - firstArgSlot() < nargs())
         {
             return true;
         }
 
         return false;
     }
 
-    // Returns true if a slot can be recovered before or during a bailout.  A
-    // definition which can be observed and recovered, implies that this
-    // definition can be optimized away as long as we can compute its values.
-    bool isRecoverableOperand(uint32_t slot) const {
-        if (isObservableFrameSlot(slot))
-            return false;
-
-        if (needsArgsObj() && isObservableArgumentSlot(slot))
-            return false;
-
-        return true;
-    }
-
   private:
     unsigned nimplicit_;
     unsigned nargs_;
     unsigned nbodyfixed_;
     unsigned nlocals_;
     unsigned nstack_;
     unsigned nslots_;
     unsigned fixedLexicalBegin_;
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2896,27 +2896,16 @@ void
 jit::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses,
                 bool cancelOffThread)
 {
     jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses,
                     cancelOffThread);
 }
 
 bool
-jit::IonScript::invalidate(JSContext *cx, bool resetUses, const char *reason)
-{
-    JitSpew(JitSpew_IonInvalidate, " Invalidate IonScript %p: %s", this, reason);
-    Vector<types::RecompileInfo> list(cx);
-    if (!list.append(recompileInfo()))
-        return false;
-    Invalidate(cx, list, resetUses, true);
-    return true;
-}
-
-bool
 jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses,
                 bool cancelOffThread)
 {
     JS_ASSERT(script->hasIonScript());
 
     if (cx->runtime()->spsProfiler.enabled()) {
         // Register invalidation with profiler.
         // Format of event payload string:
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -576,20 +576,16 @@ struct IonScript
     void copyCallTargetEntries(JSScript **callTargets);
     void copyPatchableBackedges(JSContext *cx, JitCode *code,
                                 PatchableBackedgeInfo *backedges,
                                 MacroAssembler &masm);
 
     bool invalidated() const {
         return refcount_ != 0;
     }
-
-    // Invalidate the current compilation.
-    bool invalidate(JSContext *cx, bool resetUses, const char *reason);
-
     size_t refcount() const {
         return refcount_;
     }
     void incref() {
         refcount_++;
     }
     void decref(FreeOp *fop) {
         JS_ASSERT(refcount_);
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -1279,17 +1279,16 @@ MarkJitActivation(JSTracer *trc, const J
         // GC can modify spilled registers, breaking our register checks.
         // To handle this, we disable these checks for the current VM call
         // when a GC happens.
         activation->setCheckRegs(false);
     }
 #endif
 
     activation->markRematerializedFrames(trc);
-    activation->markIonRecovery(trc);
 
     for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
         switch (frames.type()) {
           case JitFrame_Exit:
             MarkJitExitFrame(trc, frames);
             break;
           case JitFrame_BaselineJS:
             frames.baselineFrame()->trace(trc, frames);
@@ -1432,90 +1431,16 @@ uint32_t
 OsiIndex::returnPointDisplacement() const
 {
     // In general, pointer arithmetic on code is bad, but in this case,
     // getting the return address from a call instruction, stepping over pools
     // would be wrong.
     return callPointDisplacement_ + Assembler::PatchWrite_NearCallSize();
 }
 
-RInstructionResults::RInstructionResults()
-  : results_(nullptr),
-    len_(0),
-    fp_(nullptr)
-{
-}
-
-RInstructionResults::RInstructionResults(RInstructionResults&& src)
-  : results_(mozilla::Move(src.results_)),
-    len_(src.len_),
-    fp_(src.fp_)
-{
-    src.len_ = 0;
-}
-
-RInstructionResults&
-RInstructionResults::operator=(RInstructionResults&& rhs)
-{
-    MOZ_ASSERT(&rhs != this, "self-moves are prohibited");
-    this->~RInstructionResults();
-    new(this) RInstructionResults(mozilla::Move(rhs));
-    return *this;
-}
-
-RInstructionResults::~RInstructionResults()
-{
-    // results_ is freed by the UniquePtr.
-}
-
-bool
-RInstructionResults::init(JSContext *cx, uint32_t numResults, IonJSFrameLayout *fp)
-{
-    results_ = cx->make_pod_array<HeapValue>(numResults);
-    if (!results_)
-        return false;
-
-    len_ = numResults;
-
-    Value guard = MagicValue(JS_ION_BAILOUT);
-    for (size_t i = 0; i < numResults; i++)
-        results_.get()[i].init(guard);
-
-    fp_ = fp;
-    return true;
-}
-
-bool
-RInstructionResults::isInitialized() const
-{
-    MOZ_ASSERT_IF(results_, fp_);
-    return results_;
-}
-
-IonJSFrameLayout *
-RInstructionResults::frame() const
-{
-    MOZ_ASSERT(isInitialized());
-    return fp_;
-}
-
-HeapValue&
-RInstructionResults::operator [](size_t index)
-{
-    MOZ_ASSERT(index < len_);
-    return results_.get()[index];
-}
-
-void
-RInstructionResults::trace(JSTracer *trc)
-{
-    gc::MarkValueRange(trc, len_, results_.get(), "ion-recover-results");
-}
-
-
 SnapshotIterator::SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshotOffset,
                                    IonJSFrameLayout *fp, const MachineState &machine)
   : snapshot_(ionScript->snapshots(),
               snapshotOffset,
               ionScript->snapshotsRVATableSize(),
               ionScript->snapshotsListSize()),
     recover_(snapshot_,
              ionScript->recovers(),
@@ -1769,99 +1694,43 @@ SnapshotIterator::skipInstruction()
     MOZ_ASSERT(snapshot_.numAllocationsRead() == 0);
     size_t numOperands = instruction()->numOperands();
     for (size_t i = 0; i < numOperands; i++)
         skip();
     nextInstruction();
 }
 
 bool
-SnapshotIterator::initInstructionResults(MaybeReadFallback &fallback)
-{
-    MOZ_ASSERT(fallback.canRecoverResults());
-    JSContext *cx = fallback.maybeCx;
-
-    // If there is only one resume point in the list of instructions, then there
-    // is no instruction to recover, and thus no need to register any results.
-    if (recover_.numInstructions() == 1)
-        return true;
-
-    IonJSFrameLayout *fp = fallback.frame->jsFrame();
-    RInstructionResults *results = fallback.activation->maybeIonFrameRecovery(fp);
-    if (!results) {
-        // We do not have the result yet, which means that an observable stack
-        // slot is requested.  As we do not want to bailout every time for the
-        // same reason, we need to recompile without optimizing away the
-        // observable stack slots.  The script would later be recompiled to have
-        // support for Argument objects.
-        if (!ionScript_->invalidate(cx, /* resetUses = */ false, "Observe recovered instruction."))
-            return false;
-
-        // Start a new snapshot at the beginning of the JitFrameIterator.  This
-        // SnapshotIterator is used for evaluating the content of all recover
-        // instructions.  The result is then saved on the JitActivation.
-        SnapshotIterator s(*fallback.frame);
-        RInstructionResults tmp;
-        if (!s.initInstructionResults(cx, &tmp))
-            return false;
-
-        // Register the list of result on the activation.
-        if (!fallback.activation->registerIonFrameRecovery(fallback.frame->jsFrame(),
-                                                           mozilla::Move(tmp)))
-            return false;
-
-        results = fallback.activation->maybeIonFrameRecovery(fp);
-    }
-
-    MOZ_ASSERT(results->isInitialized());
-    instructionResults_ = results;
-    return true;
-}
-
-bool
-SnapshotIterator::initInstructionResults(JSContext *cx, RInstructionResults *results)
+SnapshotIterator::initIntructionResults(AutoValueVector &results)
 {
     MOZ_ASSERT(recover_.numInstructionsRead() == 1);
 
     // The last instruction will always be a resume point, no need to allocate
     // space for it.
     if (recover_.numInstructions() == 1)
         return true;
 
     MOZ_ASSERT(recover_.numInstructions() > 1);
     size_t numResults = recover_.numInstructions() - 1;
-    instructionResults_ = results;
-    if (!instructionResults_->isInitialized()) {
-        if (!instructionResults_->init(cx, numResults, fp_))
-            return false;
+    if (!results.reserve(numResults))
+        return false;
 
-        // Fill with the results of recover instructions.
-        SnapshotIterator s(*this);
-        while (s.moreInstructions()) {
-            // Skip resume point and only interpret recover instructions.
-            if (s.instruction()->isResumePoint()) {
-                s.skipInstruction();
-                continue;
-            }
+    for (size_t i = 0; i < numResults; i++)
+        results.infallibleAppend(MagicValue(JS_ION_BAILOUT));
 
-            if (!s.instruction()->recover(cx, s))
-                return false;
-            s.nextInstruction();
-        }
-    }
-
+    instructionResults_ = &results;
     return true;
 }
 
 void
 SnapshotIterator::storeInstructionResult(Value v)
 {
     uint32_t currIns = recover_.numInstructionsRead() - 1;
     MOZ_ASSERT((*instructionResults_)[currIns].isMagic(JS_ION_BAILOUT));
-    (*instructionResults_)[currIns] = v;
+    (*instructionResults_)[currIns].set(v);
 }
 
 Value
 SnapshotIterator::fromInstructionResult(uint32_t index) const
 {
     MOZ_ASSERT(!(*instructionResults_)[index].isMagic(JS_ION_BAILOUT));
     return (*instructionResults_)[index];
 }
@@ -2250,38 +2119,37 @@ InlineFrameIterator::dump() const
     fprintf(stderr, "  script = %p, pc = %p\n", (void*) script(), pc());
     fprintf(stderr, "  current op: %s\n", js_CodeName[*pc()]);
 
     if (!more()) {
         numActualArgs();
     }
 
     SnapshotIterator si = snapshotIterator();
-    MaybeReadFallback fallback(UndefinedValue());
     fprintf(stderr, "  slots: %u\n", si.numAllocations() - 1);
     for (unsigned i = 0; i < si.numAllocations() - 1; i++) {
         if (isFunction) {
             if (i == 0)
                 fprintf(stderr, "  scope chain: ");
             else if (i == 1)
                 fprintf(stderr, "  this: ");
             else if (i - 2 < callee()->nargs())
                 fprintf(stderr, "  formal (arg %d): ", i - 2);
             else {
                 if (i - 2 == callee()->nargs() && numActualArgs() > callee()->nargs()) {
                     DumpOp d(callee()->nargs());
-                    unaliasedForEachActual(GetJSContextFromJitCode(), d, ReadFrame_Overflown, fallback);
+                    unaliasedForEachActual(GetJSContextFromJitCode(), d, ReadFrame_Overflown);
                 }
 
                 fprintf(stderr, "  slot %d: ", int(i - 2 - callee()->nargs()));
             }
         } else
             fprintf(stderr, "  slot %u: ", i);
 #ifdef DEBUG
-        js_DumpValue(si.maybeRead(fallback));
+        js_DumpValue(si.maybeRead());
 #else
         fprintf(stderr, "?\n");
 #endif
     }
 
     fputc('\n', stderr);
 }
 
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -261,85 +261,30 @@ class JitFrameIterator
 
 #ifdef DEBUG
     bool verifyReturnAddressUsingNativeToBytecodeMap();
 #else
     inline bool verifyReturnAddressUsingNativeToBytecodeMap() { return true; }
 #endif
 };
 
-class RInstructionResults
-{
-    // Vector of results of recover instructions.
-    mozilla::UniquePtr<HeapValue[], JS::FreePolicy> results_;
-
-    // Length of the |results_| vector.
-    uint32_t len_;
-
-    // The frame pointer is used as a key to check if the current frame already
-    // bailed out.
-    IonJSFrameLayout *fp_;
-
-  public:
-    RInstructionResults();
-    RInstructionResults(RInstructionResults&& src);
-
-    RInstructionResults& operator=(RInstructionResults&& rhs);
-
-    ~RInstructionResults();
-
-    bool init(JSContext *cx, uint32_t numResults, IonJSFrameLayout *fp);
-    bool isInitialized() const;
-
-    IonJSFrameLayout *frame() const;
-
-    HeapValue& operator[](size_t index);
-
-    void trace(JSTracer *trc);
-};
-
-struct MaybeReadFallback
-{
-    JSContext *maybeCx;
-    JitActivation *activation;
-    JitFrameIterator *frame;
-    const Value unreadablePlaceholder;
-
-    MaybeReadFallback(const Value &placeholder = UndefinedValue())
-      : maybeCx(nullptr),
-        activation(nullptr),
-        frame(nullptr),
-        unreadablePlaceholder(placeholder)
-    {
-    }
-
-    MaybeReadFallback(JSContext *cx, JitActivation *activation, JitFrameIterator *frame)
-      : maybeCx(cx),
-        activation(activation),
-        frame(frame),
-        unreadablePlaceholder(UndefinedValue())
-    {
-    }
-
-    bool canRecoverResults() { return maybeCx; }
-};
-
+class IonJSFrameLayout;
 
 class RResumePoint;
 
 // Reads frame information in snapshot-encoding order (that is, outermost frame
 // to innermost frame).
 class SnapshotIterator
 {
     SnapshotReader snapshot_;
     RecoverReader recover_;
     IonJSFrameLayout *fp_;
     MachineState machine_;
     IonScript *ionScript_;
-    RInstructionResults *instructionResults_;
+    AutoValueVector *instructionResults_;
 
   private:
     // Read a spilled register from the machine state.
     bool hasRegister(Register reg) const {
         return machine_.has(reg);
     }
     uintptr_t fromRegister(Register reg) const {
         return machine_.read(reg);
@@ -421,18 +366,17 @@ class SnapshotIterator
     inline bool moreInstructions() const {
         return recover_.moreInstructions();
     }
 
     // Register a vector used for storing the results of the evaluation of
     // recover instructions. This vector should be registered before the
     // beginning of the iteration. This function is in charge of allocating
     // enough space for all instructions results, and return false iff it fails.
-    bool initInstructionResults(MaybeReadFallback &fallback);
-    bool initInstructionResults(JSContext *cx, RInstructionResults *results);
+    bool initIntructionResults(AutoValueVector &results);
 
     void storeInstructionResult(Value v);
 
   public:
     // Handle iterating over frames of the snapshots.
     void nextFrame();
     void settleOnFrame();
 
@@ -450,33 +394,23 @@ class SnapshotIterator
                      IonJSFrameLayout *fp, const MachineState &machine);
     explicit SnapshotIterator(const JitFrameIterator &iter);
     explicit SnapshotIterator(const IonBailoutIterator &iter);
     SnapshotIterator();
 
     Value read() {
         return allocationValue(readAllocation());
     }
-
-    Value maybeRead(MaybeReadFallback &fallback) {
+    Value maybeRead(const Value &placeholder = UndefinedValue(), bool silentFailure = false) {
         RValueAllocation a = readAllocation();
         if (allocationReadable(a))
             return allocationValue(a);
-
-        if (fallback.canRecoverResults()) {
-            if (!initInstructionResults(fallback))
-                return fallback.unreadablePlaceholder;
-
-            if (allocationReadable(a))
-                return allocationValue(a);
-
-            MOZ_ASSERT_UNREACHABLE("All allocations should be readable.");
-        }
-
-        return fallback.unreadablePlaceholder;
+        if (!silentFailure)
+            warnUnreadableAllocation();
+        return placeholder;
     }
 
     void readCommonFrameSlots(Value *scopeChain, Value *rval) {
         if (scopeChain)
             *scopeChain = read();
         else
             skip();
 
@@ -484,17 +418,18 @@ class SnapshotIterator
             *rval = read();
         else
             skip();
     }
 
     template <class Op>
     void readFunctionFrameArgs(Op &op, ArgumentsObject **argsObj, Value *thisv,
                                unsigned start, unsigned end, JSScript *script,
-                               MaybeReadFallback &fallback)
+                               const Value &unreadablePlaceholder = UndefinedValue(),
+                               bool silentFailure = false)
     {
         // Assumes that the common frame arguments have already been read.
         if (script->argumentsHasVarBinding()) {
             if (argsObj) {
                 Value v = read();
                 if (v.isObject())
                     *argsObj = &v.toObject().as<ArgumentsObject>();
             } else {
@@ -512,29 +447,28 @@ class SnapshotIterator
             i = start;
 
         for (; i < start; i++)
             skip();
         for (; i < end; i++) {
             // We are not always able to read values from the snapshots, some values
             // such as non-gc things may still be live in registers and cause an
             // error while reading the machine state.
-            Value v = maybeRead(fallback);
+            Value v = maybeRead(unreadablePlaceholder, silentFailure);
             op(v);
         }
     }
 
     Value maybeReadAllocByIndex(size_t index) {
         while (index--) {
             JS_ASSERT(moreAllocations());
             skip();
         }
 
-        MaybeReadFallback fallback(UndefinedValue());
-        Value s = maybeRead(fallback);
+        Value s = maybeRead(/* placeholder = */ UndefinedValue(), true);
 
         while (moreAllocations())
             skip();
 
         return s;
     }
 
 #ifdef TRACK_SNAPSHOTS
@@ -601,17 +535,18 @@ class InlineFrameIterator
         return frame_->numActualArgs();
     }
 
     template <class ArgOp, class LocalOp>
     void readFrameArgsAndLocals(ThreadSafeContext *cx, ArgOp &argOp, LocalOp &localOp,
                                 JSObject **scopeChain, Value *rval,
                                 ArgumentsObject **argsObj, Value *thisv,
                                 ReadFrameArgsBehavior behavior,
-                                MaybeReadFallback &fallback) const
+                                const Value &unreadablePlaceholder = UndefinedValue(),
+                                bool silentFailure = false) const
     {
         SnapshotIterator s(si_);
 
         // Read frame slots common to both function and global frames.
         Value scopeChainValue;
         s.readCommonFrameSlots(&scopeChainValue, rval);
 
         if (scopeChain)
@@ -620,18 +555,20 @@ class InlineFrameIterator
         // Read arguments, which only function frames have.
         if (isFunctionFrame()) {
             unsigned nactual = numActualArgs();
             unsigned nformal = callee()->nargs();
 
             // Get the non overflown arguments, which are taken from the inlined
             // frame, because it will have the updated value when JSOP_SETARG is
             // done.
-            if (behavior != ReadFrame_Overflown)
-                s.readFunctionFrameArgs(argOp, argsObj, thisv, 0, nformal, script(), fallback);
+            if (behavior != ReadFrame_Overflown) {
+                s.readFunctionFrameArgs(argOp, argsObj, thisv, 0, nformal, script(),
+                                        unreadablePlaceholder, silentFailure);
+            }
 
             if (behavior != ReadFrame_Formals) {
                 if (more()) {
                     // There is still a parent frame of this inlined frame.  All
                     // arguments (also the overflown) are the last pushed values
                     // in the parent frame.  To get the overflown arguments, we
                     // need to take them from there.
 
@@ -650,17 +587,17 @@ class InlineFrameIterator
                     unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj;
                     for (unsigned j = 0; j < skip; j++)
                         parent_s.skip();
 
                     // Get the overflown arguments
                     parent_s.readCommonFrameSlots(nullptr, nullptr);
                     parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr,
                                                    nformal, nactual, it.script(),
-                                                   fallback);
+                                                   unreadablePlaceholder, silentFailure);
                 } else {
                     // There is no parent frame to this inlined frame, we can read
                     // from the frame's Value vector directly.
                     Value *argv = frame_->actualArgs();
                     for (unsigned i = nformal; i < nactual; i++)
                         argOp(argv[i]);
                 }
             }
@@ -669,27 +606,26 @@ class InlineFrameIterator
         // At this point we've read all the formals in s, and can read the
         // locals.
         for (unsigned i = 0; i < script()->nfixed(); i++) {
             // We have to use maybeRead here, some of these might be recover
             // instructions, and currently InlineFrameIter does not support
             // recovering slots.
             //
             // FIXME bug 1029963.
-            localOp(s.maybeRead(fallback));
+            localOp(s.maybeRead(unreadablePlaceholder, silentFailure));
         }
     }
 
     template <class Op>
     void unaliasedForEachActual(ThreadSafeContext *cx, Op op,
-                                ReadFrameArgsBehavior behavior,
-                                MaybeReadFallback &fallback) const
+                                ReadFrameArgsBehavior behavior) const
     {
         Nop nop;
-        readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr, behavior, fallback);
+        readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr, behavior);
     }
 
     JSScript *script() const {
         return script_;
     }
     jsbytecode *pc() const {
         return pc_;
     }
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -494,17 +494,17 @@ MDefinition::hasLiveDefUses() const
 {
     for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
         MNode *ins = (*i)->consumer();
         if (ins->isDefinition()) {
             if (!ins->toDefinition()->isRecoveredOnBailout())
                 return true;
         } else {
             MOZ_ASSERT(ins->isResumePoint());
-            if (!ins->toResumePoint()->isRecoverableOperand(*i))
+            if (ins->toResumePoint()->isObservableOperand(*i))
                 return true;
         }
     }
 
     return false;
 }
 
 void
@@ -2615,22 +2615,16 @@ MResumePoint::isObservableOperand(MUse *
 }
 
 bool
 MResumePoint::isObservableOperand(size_t index) const
 {
     return block()->info().isObservableSlot(index);
 }
 
-bool
-MResumePoint::isRecoverableOperand(MUse *u) const
-{
-    return block()->info().isRecoverableOperand(indexOf(u));
-}
-
 MDefinition *
 MToInt32::foldsTo(TempAllocator &alloc)
 {
     MDefinition *input = getOperand(0);
     if (input->type() == MIRType_Int32)
         return input;
     return this;
 }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -10743,17 +10743,16 @@ class MResumePoint MOZ_FINAL :
         operands_[index].initUnchecked(operand, this);
     }
     void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
         operands_[index].replaceProducer(operand);
     }
 
     bool isObservableOperand(MUse *u) const;
     bool isObservableOperand(size_t index) const;
-    bool isRecoverableOperand(MUse *u) const;
 
     MDefinition *getOperand(size_t index) const {
         return operands_[index].producer();
     }
     jsbytecode *pc() const {
         return pc_;
     }
     uint32_t stackDepth() const {
--- a/js/src/jit/RematerializedFrame.cpp
+++ b/js/src/jit/RematerializedFrame.cpp
@@ -33,20 +33,19 @@ RematerializedFrame::RematerializedFrame
   : prevUpToDate_(false),
     top_(top),
     pc_(iter.pc()),
     frameNo_(iter.frameNo()),
     numActualArgs_(numActualArgs),
     script_(iter.script())
 {
     CopyValueToRematerializedFrame op(slots_);
-    MaybeReadFallback fallback(MagicValue(JS_OPTIMIZED_OUT));
     iter.readFrameArgsAndLocals(cx, op, op, &scopeChain_, &returnValue_,
                                 &argsObj_, &thisValue_, ReadFrame_Actuals,
-                                fallback);
+                                MagicValue(JS_OPTIMIZED_OUT), /* silentFailure = */ true);
 }
 
 /* static */ RematerializedFrame *
 RematerializedFrame::New(ThreadSafeContext *cx, uint8_t *top, InlineFrameIterator &iter)
 {
     unsigned numFormals = iter.isFunctionFrame() ? iter.callee()->nargs() : 0;
     unsigned numActualArgs = Max(numFormals, iter.numActualArgs());
     size_t numBytes = sizeof(RematerializedFrame) +
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -141,18 +141,18 @@ IsObjectEscaped(MInstruction *ins)
 
     // Check if the object is escaped. If the object is not the first argument
     // of either a known Store / Load, then we consider it as escaped. This is a
     // cheap and conservative escape analysis.
     for (MUseIterator i(ins->usesBegin()); i != ins->usesEnd(); i++) {
         MNode *consumer = (*i)->consumer();
         if (!consumer->isDefinition()) {
             // Cannot optimize if it is observable from fun.arguments or others.
-            if (!consumer->toResumePoint()->isRecoverableOperand(*i)) {
-                JitSpewDef(JitSpew_Escape, "Observable object cannot be recovered\n", ins);
+            if (consumer->toResumePoint()->isObservableOperand(*i)) {
+                JitSpewDef(JitSpew_Escape, "Object is observable\n", ins);
                 return true;
             }
             continue;
         }
 
         MDefinition *def = consumer->toDefinition();
         switch (def->op()) {
           case MDefinition::Op_StoreFixedSlot:
@@ -520,18 +520,18 @@ IsArrayEscaped(MInstruction *ins)
 
     // Check if the object is escaped. If the object is not the first argument
     // of either a known Store / Load, then we consider it as escaped. This is a
     // cheap and conservative escape analysis.
     for (MUseIterator i(ins->usesBegin()); i != ins->usesEnd(); i++) {
         MNode *consumer = (*i)->consumer();
         if (!consumer->isDefinition()) {
             // Cannot optimize if it is observable from fun.arguments or others.
-            if (!consumer->toResumePoint()->isRecoverableOperand(*i)) {
-                JitSpewDef(JitSpew_Escape, "Observable array cannot be recovered\n", ins);
+            if (consumer->toResumePoint()->isObservableOperand(*i)) {
+                JitSpewDef(JitSpew_Escape, "Array is observable\n", ins);
                 return true;
             }
             continue;
         }
 
         MDefinition *def = consumer->toDefinition();
         switch (def->op()) {
           case MDefinition::Op_Elements: {
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -344,18 +344,17 @@ FrameIter::unaliasedForEachActual(JSCont
       case DONE:
       case ASMJS:
         break;
       case INTERP:
         interpFrame()->unaliasedForEachActual(op);
         return;
       case JIT:
         if (data_.jitFrames_.isIonJS()) {
-            jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_);
-            ionInlineFrames_.unaliasedForEachActual(cx, op, jit::ReadFrame_Actuals, recover);
+            ionInlineFrames_.unaliasedForEachActual(cx, op, jit::ReadFrame_Actuals);
         } else {
             JS_ASSERT(data_.jitFrames_.isBaselineJS());
             data_.jitFrames_.unaliasedForEachActual(op, jit::ReadFrame_Actuals);
         }
         return;
     }
     MOZ_CRASH("Unexpected state");
 }
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1396,50 +1396,46 @@ js::CheckLocalUnaliased(MaybeCheckAliasi
         // current frame, and so they do not know the block scope.
     }
 }
 #endif
 
 jit::JitActivation::JitActivation(JSContext *cx, bool active)
   : Activation(cx, Jit),
     active_(active),
-    rematerializedFrames_(nullptr),
-    ionRecovery_(cx)
+    rematerializedFrames_(nullptr)
 {
     if (active) {
         prevJitTop_ = cx->mainThread().jitTop;
         prevJitJSContext_ = cx->mainThread().jitJSContext;
         cx->mainThread().jitJSContext = cx;
     } else {
         prevJitTop_ = nullptr;
         prevJitJSContext_ = nullptr;
     }
 }
 
 jit::JitActivation::JitActivation(ForkJoinContext *cx)
   : Activation(cx, Jit),
     active_(true),
-    rematerializedFrames_(nullptr),
-    ionRecovery_(cx)
+    rematerializedFrames_(nullptr)
 {
     prevJitTop_ = cx->perThreadData->jitTop;
     prevJitJSContext_ = cx->perThreadData->jitJSContext;
     cx->perThreadData->jitJSContext = nullptr;
 }
 
 jit::JitActivation::~JitActivation()
 {
     if (active_) {
         cx_->perThreadData->jitTop = prevJitTop_;
         cx_->perThreadData->jitJSContext = prevJitJSContext_;
     }
 
     clearRematerializedFrames();
-    // All reocvered value are taken from activation during the bailout.
-    MOZ_ASSERT(ionRecovery_.empty());
     js_delete(rematerializedFrames_);
 }
 
 // setActive() is inlined in GenerateFFIIonExit() with explicit masm instructions so
 // changes to the logic here need to be reflected in GenerateFFIIonExit() in the enable
 // and disable activation instruction sequences.
 void
 jit::JitActivation::setActive(JSContext *cx, bool active)
@@ -1544,60 +1540,16 @@ void
 jit::JitActivation::markRematerializedFrames(JSTracer *trc)
 {
     if (!rematerializedFrames_)
         return;
     for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront())
         RematerializedFrame::MarkInVector(trc, e.front().value());
 }
 
-bool
-jit::JitActivation::registerIonFrameRecovery(IonJSFrameLayout *fp, RInstructionResults&& results)
-{
-#ifdef DEBUG
-    // Check that there is no entry in the vector yet.
-    RInstructionResults *tmp = maybeIonFrameRecovery(fp);
-    MOZ_ASSERT_IF(tmp, tmp->isInitialized());
-#endif
-
-    if (!ionRecovery_.append(mozilla::Move(results)))
-        return false;
-
-    return true;
-}
-
-jit::RInstructionResults *
-jit::JitActivation::maybeIonFrameRecovery(IonJSFrameLayout *fp)
-{
-    for (RInstructionResults *it = ionRecovery_.begin(); it != ionRecovery_.end(); ) {
-        if (it->frame() == fp)
-            return it;
-    }
-
-    return nullptr;
-}
-
-void
-jit::JitActivation::maybeTakeIonFrameRecovery(IonJSFrameLayout *fp, RInstructionResults *results)
-{
-    RInstructionResults *elem = maybeIonFrameRecovery(fp);
-    if (!elem)
-        return;
-
-    *results = mozilla::Move(*elem);
-    ionRecovery_.erase(elem);
-}
-
-void
-jit::JitActivation::markIonRecovery(JSTracer *trc)
-{
-    for (RInstructionResults *it = ionRecovery_.begin(); it != ionRecovery_.end(); it++)
-        it->trace(trc);
-}
-
 AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
   : Activation(cx, AsmJS),
     module_(module),
     entrySP_(nullptr),
     profiler_(nullptr),
     resumePC_(nullptr),
     fp_(nullptr),
     exitReason_(AsmJSExit::None)
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1313,26 +1313,16 @@ class JitActivation : public Activation
     // frame pointers (i.e. jitTop) to a vector of rematerializations of all
     // inline frames associated with that frame.
     //
     // This table is lazily initialized by calling getRematerializedFrame.
     typedef Vector<RematerializedFrame *> RematerializedFrameVector;
     typedef HashMap<uint8_t *, RematerializedFrameVector> RematerializedFrameTable;
     RematerializedFrameTable *rematerializedFrames_;
 
-    // This vector is used to remember the outcome of the evaluation of recover
-    // instructions.
-    //
-    // RInstructionResults are appended into this vector when Snapshot values
-    // have to be read, or when the evaluation has to run before some mutating
-    // code.  Each RInstructionResults belongs to one frame which has to bailout
-    // as soon as we get back to it.
-    typedef Vector<RInstructionResults, 1> IonRecoveryMap;
-    IonRecoveryMap ionRecovery_;
-
     void clearRematerializedFrames();
 
 #ifdef CHECK_OSIPOINT_REGISTERS
   protected:
     // Used to verify that live registers don't change between a VM call and
     // the OsiPoint that follows it. Protected to silence Clang warning.
     uint32_t checkRegs_;
     RegisterDump regs_;
@@ -1397,30 +1387,16 @@ class JitActivation : public Activation
     bool hasRematerializedFrame(uint8_t *top, size_t inlineDepth = 0) {
         return !!lookupRematerializedFrame(top, inlineDepth);
     }
 
     // Remove a previous rematerialization by fp.
     void removeRematerializedFrame(uint8_t *top);
 
     void markRematerializedFrames(JSTracer *trc);
-
-
-    // Register the results of on Ion frame recovery.
-    bool registerIonFrameRecovery(IonJSFrameLayout *fp, RInstructionResults&& results);
-
-    // Return the pointer to the Ion frame recovery, if it is already registered.
-    RInstructionResults *maybeIonFrameRecovery(IonJSFrameLayout *fp);
-
-    // If an Ion frame recovery exists for the |fp| frame exists on the
-    // activation, then move its content to the |results| argument, and remove
-    // it from the activation.
-    void maybeTakeIonFrameRecovery(IonJSFrameLayout *fp, RInstructionResults *results);
-
-    void markIonRecovery(JSTracer *trc);
 };
 
 // A filtering of the ActivationIterator to only stop at JitActivations.
 class JitActivationIterator : public ActivationIterator
 {
     void settle() {
         while (!done() && !activation_->isJit())
             ActivationIterator::operator++();