Bug 716647 - Part 3: Support rematerializing Ion frames on the stack. (r=jandem)
authorShu-yu Guo <shu@rfrn.org>
Thu, 24 Apr 2014 01:59:37 -0700
changeset 180263 061ebab47be320047966424d449c90de296ea930
parent 180262 b30afb9de404554b547a9adba6bc483fd045a584
child 180264 a19a7c0a4b04602d66c3ecf3c956a6cee8ba6559
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersjandem
bugs716647
milestone31.0a1
Bug 716647 - Part 3: Support rematerializing Ion frames on the stack. (r=jandem)
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jit/IonFrames.cpp
js/src/jit/JitFrameIterator.h
js/src/jit/RematerializedFrame.cpp
js/src/jit/RematerializedFrame.h
js/src/jsproxy.cpp
js/src/moz.build
js/src/vm/Debugger.cpp
js/src/vm/OldDebugAPI.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -3004,16 +3004,32 @@ jit::TraceIonScripts(JSTracer* trc, JSSc
 
     if (script->hasParallelIonScript())
         jit::IonScript::Trace(trc, script->parallelIonScript());
 
     if (script->hasBaselineScript())
         jit::BaselineScript::Trace(trc, script->baselineScript());
 }
 
+bool
+jit::RematerializeAllFrames(JSContext *cx, JSCompartment *comp)
+{
+    for (JitActivationIterator iter(comp->runtimeFromMainThread()); !iter.done(); ++iter) {
+        if (iter.activation()->compartment() == comp) {
+            for (JitFrameIterator frameIter(iter); !frameIter.done(); ++frameIter) {
+                if (!frameIter.isIonJS())
+                    continue;
+                if (!iter.activation()->asJit()->getRematerializedFrame(cx, frameIter))
+                    return false;
+            }
+        }
+    }
+    return true;
+}
+
 AutoDebugModeInvalidation::~AutoDebugModeInvalidation()
 {
     MOZ_ASSERT(!!comp_ != !!zone_);
 
     if (needInvalidation_ == NoNeed)
         return;
 
     Zone *zone = zone_ ? zone_ : comp_->zone();
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -183,14 +183,16 @@ void ForbidCompilation(JSContext *cx, JS
 
 void PurgeCaches(JSScript *script, JS::Zone *zone);
 size_t SizeOfIonData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf);
 void DestroyIonScripts(FreeOp *fop, JSScript *script);
 void TraceIonScripts(JSTracer* trc, JSScript *script);
 
 void RequestInterruptForIonCode(JSRuntime *rt, JSRuntime::InterruptMode mode);
 
+bool RematerializeAllFrames(JSContext *cx, JSCompartment *comp);
+
 } // namespace jit
 } // namespace js
 
 #endif // JS_ION
 
 #endif /* jit_Ion_h */
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -19,19 +19,21 @@
 #include "jit/IonSpewer.h"
 #include "jit/JitCompartment.h"
 #include "jit/ParallelFunctions.h"
 #include "jit/PcScriptCache.h"
 #include "jit/Recover.h"
 #include "jit/Safepoints.h"
 #include "jit/Snapshots.h"
 #include "jit/VMFunctions.h"
+#include "vm/ArgumentsObject.h"
 #include "vm/ForkJoin.h"
 #include "vm/Interpreter.h"
 
+#include "jsscriptinlines.h"
 #include "jit/JitFrameIterator-inl.h"
 #include "vm/Probes-inl.h"
 
 namespace js {
 namespace jit {
 
 // Given a slot index, returns the offset, in bytes, of that slot from an
 // IonJSFrameLayout. Slot distances are uniform across architectures, however,
@@ -1141,26 +1143,29 @@ MarkRectifierFrame(JSTracer *trc, const 
     // it if we're calling a constructor that returns a primitive value.
     IonRectifierFrameLayout *layout = (IonRectifierFrameLayout *)frame.fp();
     gc::MarkValueRoot(trc, &layout->argv()[0], "ion-thisv");
 }
 
 static void
 MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations)
 {
+    JitActivation *activation = activations.activation()->asJit();
+
 #ifdef CHECK_OSIPOINT_REGISTERS
     if (js_JitOptions.checkOsiPointRegisters) {
         // 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.
-        JitActivation *activation = activations.activation()->asJit();
         activation->setCheckRegs(false);
     }
 #endif
 
+    activation->markRematerializedFrames(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);
             break;
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -113,16 +113,19 @@ class JitFrameIterator
 
     // Current frame information.
     FrameType type() const {
         return type_;
     }
     uint8_t *fp() const {
         return current_;
     }
+    const JitActivation *activation() const {
+        return activation_;
+    }
 
     IonCommonFrameLayout *current() const {
         return (IonCommonFrameLayout *)current_;
     }
 
     inline uint8_t *returnAddress() const;
 
     IonJSFrameLayout *jsFrame() const {
@@ -362,31 +365,42 @@ class SnapshotIterator
         RValueAllocation a = readAllocation();
         if (allocationReadable(a))
             return allocationValue(a);
         if (!silentFailure)
             warnUnreadableAllocation();
         return UndefinedValue();
     }
 
-    template <class Op>
-    void readFrameArgs(Op &op, Value *scopeChain, Value *thisv,
-                       unsigned start, unsigned end, JSScript *script)
-    {
+    void readCommonFrameSlots(Value *scopeChain, Value *rval) {
         if (scopeChain)
             *scopeChain = read();
         else
             skip();
 
-        // Skip slot for return value.
-        skip();
+        if (rval)
+            *rval = read();
+        else
+            skip();
+    }
 
-        // Skip slot for arguments object.
-        if (script->argumentsHasVarBinding())
-            skip();
+    template <class Op>
+    void readFunctionFrameArgs(Op &op, ArgumentsObject **argsObj, Value *thisv,
+                               unsigned start, unsigned end, JSScript *script)
+    {
+        // 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 {
+                skip();
+            }
+        }
 
         if (thisv)
             *thisv = read();
         else
             skip();
 
         unsigned i = 0;
         if (end < start)
@@ -441,16 +455,32 @@ class InlineFrameIteratorMaybeGC
 
     struct Nop {
         void operator()(const Value &v) { }
     };
 
   private:
     void findNextFrame();
 
+    JSObject *computeScopeChain(Value scopeChainValue) const {
+        if (scopeChainValue.isObject())
+            return &scopeChainValue.toObject();
+
+        if (isFunctionFrame()) {
+            // Heavyweight functions should always have a scope chain.
+            MOZ_ASSERT(!callee()->isHeavyweight());
+            return callee()->environment();
+        }
+
+        // Ion does not handle scripts that are not compile-and-go.
+        MOZ_ASSERT(!script()->isForEval());
+        MOZ_ASSERT(script()->compileAndGo());
+        return &script()->global();
+    }
+
   public:
     InlineFrameIteratorMaybeGC(JSContext *cx, const JitFrameIterator *iter)
       : callee_(cx),
         script_(cx)
     {
         resetOn(iter);
     }
 
@@ -499,73 +529,87 @@ class InlineFrameIteratorMaybeGC
         if (more())
             return numActualArgs_;
 
         return frame_->numActualArgs();
     }
 
     template <class ArgOp, class LocalOp>
     void readFrameArgsAndLocals(JSContext *cx, ArgOp &argOp, LocalOp &localOp,
-                                Value *scopeChain, Value *thisv,
+                                JSObject **scopeChain, Value *rval,
+                                ArgumentsObject **argsObj, Value *thisv,
                                 ReadFrameArgsBehavior behavior) const
     {
-        unsigned nactual = numActualArgs();
-        unsigned nformal = callee()->nargs();
+        SnapshotIterator s(si_);
+
+        // Read frame slots common to both function and global frames.
+        Value scopeChainValue;
+        s.readCommonFrameSlots(&scopeChainValue, rval);
+
+        if (scopeChain)
+            *scopeChain = computeScopeChain(scopeChainValue);
 
-        // Get the non overflown arguments, which are taken from the inlined
-        // frame, because it will have the updated value when JSOP_SETARG is
-        // done.
-        SnapshotIterator s(si_);
-        if (behavior != ReadFrame_Overflown)
-            s.readFrameArgs(argOp, scopeChain, thisv, 0, nformal, script());
+        // Read arguments, which only function frames have.
+        if (isFunctionFrame()) {
+            unsigned nactual = numActualArgs();
+            unsigned nformal = callee()->nargs();
 
-        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.
+            // 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());
+
+            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.
 
-                // The overflown arguments are not available in current frame.
-                // They are the last pushed arguments in the parent frame of
-                // this inlined frame.
-                InlineFrameIteratorMaybeGC it(cx, this);
-                ++it;
-                unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0;
-                SnapshotIterator parent_s(it.snapshotIterator());
+                    // The overflown arguments are not available in current frame.
+                    // They are the last pushed arguments in the parent frame of
+                    // this inlined frame.
+                    InlineFrameIteratorMaybeGC it(cx, this);
+                    ++it;
+                    unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0;
+                    SnapshotIterator parent_s(it.snapshotIterator());
 
-                // Skip over all slots until we get to the last slots
-                // (= arguments slots of callee) the +3 is for [this], [returnvalue],
-                // [scopechain], and maybe +1 for [argsObj]
-                JS_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj);
-                unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj;
-                for (unsigned j = 0; j < skip; j++)
-                    parent_s.skip();
+                    // Skip over all slots until we get to the last slots
+                    // (= arguments slots of callee) the +3 is for [this], [returnvalue],
+                    // [scopechain], and maybe +1 for [argsObj]
+                    JS_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj);
+                    unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj;
+                    for (unsigned j = 0; j < skip; j++)
+                        parent_s.skip();
 
-                // Get the overflown arguments
-                parent_s.readFrameArgs(argOp, nullptr, nullptr, nformal, nactual, it.script());
-            } 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]);
+                    // Get the overflown arguments
+                    parent_s.readCommonFrameSlots(nullptr, nullptr);
+                    parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr,
+                                                   nformal, nactual, it.script());
+                } 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]);
+                }
             }
         }
 
         // At this point we've read all the formals in s, and can read the
         // locals.
         for (unsigned i = 0; i < script()->nfixed(); i++)
             localOp(s.read());
     }
 
     template <class Op>
     void unaliasedForEachActual(JSContext *cx, Op op, ReadFrameArgsBehavior behavior) const {
         Nop nop;
-        readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, behavior);
+        readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr, behavior);
     }
 
     JSScript *script() const {
         return script_;
     }
     jsbytecode *pc() const {
         return pc_;
     }
@@ -575,41 +619,42 @@ class InlineFrameIteratorMaybeGC
     bool isFunctionFrame() const;
     bool isConstructing() const;
 
     JSObject *scopeChain() const {
         SnapshotIterator s(si_);
 
         // scopeChain
         Value v = s.read();
-        if (v.isObject())
-            return &v.toObject();
-
-        return callee()->environment();
+        return computeScopeChain(v);
     }
 
     JSObject *thisObject() const {
+        // In strict modes, |this| may not be an object and thus may not be
+        // readable which can either segv in read or trigger the assertion.
+        Value v = thisValue();
+        JS_ASSERT(v.isObject());
+        return &v.toObject();
+    }
+
+    Value thisValue() const {
         // JS_ASSERT(isConstructing(...));
         SnapshotIterator s(si_);
 
         // scopeChain
         s.skip();
 
         // return value
         s.skip();
 
         // Arguments object.
         if (script()->argumentsHasVarBinding())
             s.skip();
 
-        // In strict modes, |this| may not be an object and thus may not be
-        // readable which can either segv in read or trigger the assertion.
-        Value v = s.read();
-        JS_ASSERT(v.isObject());
-        return &v.toObject();
+        return s.read();
     }
 
     InlineFrameIteratorMaybeGC &operator++() {
         findNextFrame();
         return *this;
     }
 
     void dump() const;
@@ -617,18 +662,21 @@ class InlineFrameIteratorMaybeGC
     void resetOn(const JitFrameIterator *iter);
 
     const JitFrameIterator &frame() const {
         return *frame_;
     }
 
     // Inline frame number, 0 for the outermost (non-inlined) frame.
     size_t frameNo() const {
+        return frameCount() - framesRead_;
+    }
+    size_t frameCount() const {
         MOZ_ASSERT(frameCount_ != UINT32_MAX);
-        return frameCount_ - framesRead_;
+        return frameCount_;
     }
 
   private:
     InlineFrameIteratorMaybeGC() MOZ_DELETE;
     InlineFrameIteratorMaybeGC(const InlineFrameIteratorMaybeGC &iter) MOZ_DELETE;
 };
 typedef InlineFrameIteratorMaybeGC<CanGC> InlineFrameIterator;
 typedef InlineFrameIteratorMaybeGC<NoGC> InlineFrameIteratorNoGC;
new file mode 100644
--- /dev/null
+++ b/js/src/jit/RematerializedFrame.cpp
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jit/RematerializedFrame.h"
+#include "jit/IonFrames.h"
+
+#include "vm/ArgumentsObject.h"
+
+#include "jsscriptinlines.h"
+#include "jit/IonFrames-inl.h"
+
+using namespace js;
+using namespace jit;
+
+struct CopyValueToRematerializedFrame
+{
+    Value *slots;
+
+    CopyValueToRematerializedFrame(Value *slots)
+      : slots(slots)
+    { }
+
+    void operator()(const Value &v) {
+        *slots++ = v;
+    }
+};
+
+RematerializedFrame::RematerializedFrame(JSContext *cx, uint8_t *top, InlineFrameIterator &iter)
+  : top_(top),
+    frameNo_(iter.frameNo()),
+    numActualArgs_(iter.numActualArgs()),
+    script_(iter.script())
+{
+    CopyValueToRematerializedFrame op(slots_);
+    iter.readFrameArgsAndLocals(cx, op, op, &scopeChain_, &returnValue_,
+                                &argsObj_, &thisValue_, ReadFrame_Actuals);
+}
+
+/* static */ RematerializedFrame *
+RematerializedFrame::New(JSContext *cx, uint8_t *top, InlineFrameIterator &iter)
+{
+    unsigned numFormals = iter.isFunctionFrame() ? iter.callee()->nargs() : 0;
+    size_t numBytes = sizeof(RematerializedFrame) +
+        (Max(numFormals, iter.numActualArgs()) +
+         iter.script()->nfixed()) * sizeof(Value) -
+        sizeof(Value); // 1 Value included in sizeof(RematerializedFrame)
+
+    void *buf = cx->calloc_(numBytes);
+    if (!buf)
+        return nullptr;
+
+    return new (buf) RematerializedFrame(cx, top, iter);
+}
+
+CallObject &
+RematerializedFrame::callObj() const
+{
+    JS_ASSERT(hasCallObj());
+
+    JSObject *scope = scopeChain();
+    while (!scope->is<CallObject>())
+        scope = scope->enclosingScope();
+    return scope->as<CallObject>();
+}
+
+void
+RematerializedFrame::mark(JSTracer *trc)
+{
+    gc::MarkScriptRoot(trc, &script_, "remat ion frame script");
+    gc::MarkObjectRoot(trc, &scopeChain_, "remat ion frame scope chain");
+    gc::MarkValueRoot(trc, &returnValue_, "remat ion frame return value");
+    gc::MarkValueRoot(trc, &thisValue_, "remat ion frame this");
+    gc::MarkValueRootRange(trc, slots_, slots_ + numActualArgs_ + script_->nfixed(),
+                           "remat ion frame stack");
+}
+
+void
+RematerializedFrame::dump()
+{
+    fprintf(stderr, " Rematerialized Optimized Frame%s\n", inlined() ? " (inlined)" : "");
+    if (isFunctionFrame()) {
+        fprintf(stderr, "  callee fun: ");
+#ifdef DEBUG
+        js_DumpObject(callee());
+#else
+        fprintf(stderr, "?\n");
+#endif
+    } else {
+        fprintf(stderr, "  global frame, no callee\n");
+    }
+
+    fprintf(stderr, "  file %s line %u\n",
+            script()->filename(), (unsigned) script()->lineno());
+
+    fprintf(stderr, "  script = %p\n", (void*) script());
+
+    if (isFunctionFrame()) {
+        fprintf(stderr, "  scope chain: ");
+#ifdef DEBUG
+        js_DumpObject(scopeChain());
+#else
+        fprintf(stderr, "?\n");
+#endif
+
+        if (hasArgsObj()) {
+            fprintf(stderr, "  args obj: ");
+#ifdef DEBUG
+            js_DumpObject(&argsObj());
+#else
+            fprintf(stderr, "?\n");
+#endif
+        }
+
+        fprintf(stderr, "  this: ");
+#ifdef DEBUG
+        js_DumpValue(thisValue());
+#else
+        fprintf(stderr, "?\n");
+#endif
+
+        for (unsigned i = 0; i < numActualArgs(); i++) {
+            if (i < numFormalArgs())
+                fprintf(stderr, "  formal (arg %d): ", i);
+            else
+                fprintf(stderr, "  overflown (arg %d): ", i);
+#ifdef DEBUG
+            js_DumpValue(argv()[i]);
+#else
+            fprintf(stderr, "?\n");
+#endif
+        }
+
+        for (unsigned i = 0; i < script()->nfixed(); i++) {
+            fprintf(stderr, "  local %d: ", i);
+#ifdef DEBUG
+            js_DumpValue(locals()[i]);
+#else
+            fprintf(stderr, "?\n");
+#endif
+        }
+    }
+
+    fputc('\n', stderr);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/RematerializedFrame.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_RematerializedFrame_h
+#define jit_RematerializedFrame_h
+
+#ifdef JS_ION
+
+#include "jsfun.h"
+
+#include "jit/JitFrameIterator.h"
+
+#include "vm/Stack.h"
+
+namespace js {
+namespace jit {
+
+//
+// An optimized frame that has been rematerialized with values read out of
+// Snapshots.
+//
+class RematerializedFrame
+{
+    // The fp of the top frame associated with this possibly inlined frame.
+    uint8_t *top_;
+
+    size_t frameNo_;
+    unsigned numActualArgs_;
+
+    JSScript *script_;
+    JSObject *scopeChain_;
+    ArgumentsObject *argsObj_;
+
+    Value returnValue_;
+    Value thisValue_;
+    Value slots_[1];
+
+    RematerializedFrame(JSContext *cx, uint8_t *top, InlineFrameIterator &iter);
+
+  public:
+    static RematerializedFrame *New(JSContext *cx, uint8_t *top, InlineFrameIterator &iter);
+
+    uint8_t *top() const {
+        return top_;
+    }
+    size_t frameNo() const {
+        return frameNo_;
+    }
+    bool inlined() const {
+        return frameNo_ > 0;
+    }
+
+    JSObject *scopeChain() const {
+        return scopeChain_;
+    }
+    bool hasCallObj() const {
+        return maybeFun() && fun()->isHeavyweight();
+    }
+    CallObject &callObj() const;
+
+    bool hasArgsObj() const {
+        return !!argsObj_;
+    }
+    ArgumentsObject &argsObj() const {
+        MOZ_ASSERT(hasArgsObj());
+        MOZ_ASSERT(script()->needsArgsObj());
+        return *argsObj_;
+    }
+
+    bool isFunctionFrame() const {
+        return !!script_->functionNonDelazifying();
+    }
+    bool isGlobalFrame() const {
+        return !isFunctionFrame();
+    }
+    bool isNonEvalFunctionFrame() const {
+        // Ion doesn't support eval frames.
+        return isFunctionFrame();
+    }
+
+    JSScript *script() const {
+        return script_;
+    }
+    JSFunction *fun() const {
+        MOZ_ASSERT(isFunctionFrame());
+        return script_->functionNonDelazifying();
+    }
+    JSFunction *maybeFun() const {
+        return isFunctionFrame() ? fun() : nullptr;
+    }
+    JSFunction *callee() const {
+        return fun();
+    }
+    Value calleev() const {
+        return ObjectValue(*fun());
+    }
+    Value &thisValue() {
+        return thisValue_;
+    }
+
+    unsigned numFormalArgs() const {
+        return maybeFun() ? fun()->nargs() : 0;
+    }
+    unsigned numActualArgs() const {
+        return numActualArgs_;
+    }
+
+    Value *argv() {
+        return slots_;
+    }
+    Value *locals() {
+        return slots_ + numActualArgs_;
+    }
+
+    Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+        JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i));
+        JS_ASSERT(i < script()->nfixed());
+        return locals()[i];
+    }
+    Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+        JS_ASSERT(i < script()->nfixed());
+#ifdef DEBUG
+        CheckLocalUnaliased(checkAliasing, script(), i);
+#endif
+        return locals()[i];
+    }
+    Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+        JS_ASSERT(i < numFormalArgs());
+        JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() &&
+                                    !script()->formalIsAliased(i));
+        return argv()[i];
+    }
+    Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+        JS_ASSERT(i < numActualArgs());
+        JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
+        JS_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i));
+        return argv()[i];
+    }
+
+    Value returnValue() const {
+        return returnValue_;
+    }
+
+    void mark(JSTracer *trc);
+    void dump();
+};
+
+} // namespace jit
+} // namespace js
+
+#endif // JS_ION
+#endif // jit_RematerializedFrame_h
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -317,17 +317,17 @@ BaseProxyHandler::nativeCall(JSContext *
 }
 
 bool
 BaseProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
     RootedValue val(cx, ObjectValue(*proxy.get()));
     js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
-                        JSDVG_SEARCH_STACK, val, NullPtr());
+                        JSDVG_SEARCH_STACK, val, js::NullPtr());
     return false;
 }
 
 bool
 BaseProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx)
 {
     return false;
 }
@@ -3243,17 +3243,17 @@ proxy_createFunction(JSContext *cx, unsi
             return false;
     } else {
         construct = call;
     }
 
     // Stash the call and construct traps on a holder object that we can stick
     // in a slot on the proxy.
     RootedObject ccHolder(cx, JS_NewObjectWithGivenProto(cx, Jsvalify(&CallConstructHolder),
-                                                         NullPtr(), cx->global()));
+                                                         js::NullPtr(), cx->global()));
     if (!ccHolder)
         return false;
     ccHolder->setReservedSlot(0, ObjectValue(*call));
     ccHolder->setReservedSlot(1, ObjectValue(*construct));
 
     RootedValue priv(cx, ObjectValue(*handler));
     ProxyOptions options;
     options.selectDefaultClass(true);
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -278,16 +278,17 @@ if CONFIG['ENABLE_ION']:
         'jit/MIRGraph.cpp',
         'jit/MoveResolver.cpp',
         'jit/ParallelFunctions.cpp',
         'jit/ParallelSafetyAnalysis.cpp',
         'jit/PerfSpewer.cpp',
         'jit/RangeAnalysis.cpp',
         'jit/Recover.cpp',
         'jit/RegisterAllocator.cpp',
+        'jit/RematerializedFrame.cpp',
         'jit/Safepoints.cpp',
         'jit/shared/BaselineCompiler-shared.cpp',
         'jit/shared/CodeGenerator-shared.cpp',
         'jit/shared/Lowering-shared.cpp',
         'jit/Snapshots.cpp',
         'jit/StupidAllocator.cpp',
         'jit/TypeDescrSet.cpp',
         'jit/TypePolicy.cpp',
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4214,17 +4214,17 @@ static bool
 DebuggerFrame_getThis(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_FRAME_ITER(cx, argc, vp, "get this", args, thisobj, _, iter);
     RootedValue thisv(cx);
     {
         AutoCompartment ac(cx, iter.scopeChain());
         if (!iter.computeThis(cx))
             return false;
-        thisv = iter.thisv();
+        thisv = iter.computedThisValue();
     }
     if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &thisv))
         return false;
     args.rval().set(thisv);
     return true;
 }
 
 static bool
@@ -4616,17 +4616,17 @@ DebuggerGenericEval(JSContext *cx, const
         ac.construct(cx, scope);
 
     RootedValue thisv(cx);
     Rooted<Env *> env(cx);
     if (iter) {
         /* ExecuteInEnv requires 'fp' to have a computed 'this" value. */
         if (!iter->computeThis(cx))
             return false;
-        thisv = iter->thisv();
+        thisv = iter->computedThisValue();
         env = GetDebugScopeForFrame(cx, iter->abstractFramePtr(), iter->pc());
         if (!env)
             return false;
     } else {
         /*
          * Use the global as 'this'. If the global is an inner object, it
          * should have a thisObject hook that returns the appropriate outer
          * object.
--- a/js/src/vm/OldDebugAPI.cpp
+++ b/js/src/vm/OldDebugAPI.cpp
@@ -1045,17 +1045,17 @@ FormatFrame(JSContext *cx, const NonBuil
     RootedFunction fun(cx, iter.maybeCallee());
     RootedString funname(cx);
     if (fun)
         funname = fun->atom();
 
     RootedValue thisVal(cx);
     AutoPropertyDescArray thisProps(cx);
     if (iter.computeThis(cx)) {
-        thisVal = iter.thisv();
+        thisVal = iter.computedThisValue();
         if (showThisProps && !thisVal.isPrimitive()) {
             RootedObject thisObj(cx, &thisVal.toObject());
             thisProps.fetch(thisObj);
         }
     }
 
     // print the frame number and function name
     if (funname) {
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -9,16 +9,17 @@
 
 #include "vm/Stack.h"
 
 #include "mozilla/PodOperations.h"
 
 #include "jscntxt.h"
 
 #include "jit/BaselineFrame.h"
+#include "jit/RematerializedFrame.h"
 #include "vm/ScopeObject.h"
 
 #include "jsobjinlines.h"
 
 #include "jit/BaselineFrame-inl.h"
 
 namespace js {
 
@@ -405,17 +406,19 @@ AbstractFramePtr::setReturnValue(const V
 }
 
 inline JSObject *
 AbstractFramePtr::scopeChain() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->scopeChain();
 #ifdef JS_ION
-    return asBaselineFrame()->scopeChain();
+    if (isBaselineFrame())
+        return asBaselineFrame()->scopeChain();
+    return asRematerializedFrame()->scopeChain();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 
 inline void
 AbstractFramePtr::pushOnScopeChain(ScopeObject &scope)
 {
@@ -431,17 +434,19 @@ AbstractFramePtr::pushOnScopeChain(Scope
 }
 
 inline CallObject &
 AbstractFramePtr::callObj() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->callObj();
 #ifdef JS_ION
-    return asBaselineFrame()->callObj();
+    if (isBaselineFrame())
+        return asBaselineFrame()->callObj();
+    return asRematerializedFrame()->callObj();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 
 inline bool
 AbstractFramePtr::initFunctionScopeObjects(JSContext *cx)
 {
@@ -461,88 +466,102 @@ AbstractFramePtr::compartment() const
 }
 
 inline unsigned
 AbstractFramePtr::numActualArgs() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->numActualArgs();
 #ifdef JS_ION
-    return asBaselineFrame()->numActualArgs();
+    if (isBaselineFrame())
+        return asBaselineFrame()->numActualArgs();
+    return asRematerializedFrame()->numActualArgs();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline unsigned
 AbstractFramePtr::numFormalArgs() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->numFormalArgs();
 #ifdef JS_ION
-    return asBaselineFrame()->numFormalArgs();
+    if (isBaselineFrame())
+        return asBaselineFrame()->numFormalArgs();
+    return asRematerializedFrame()->numActualArgs();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 
 inline Value &
 AbstractFramePtr::unaliasedVar(uint32_t i, MaybeCheckAliasing checkAliasing)
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->unaliasedVar(i, checkAliasing);
 #ifdef JS_ION
-    return asBaselineFrame()->unaliasedVar(i, checkAliasing);
+    if (isBaselineFrame())
+        return asBaselineFrame()->unaliasedVar(i, checkAliasing);
+    return asRematerializedFrame()->unaliasedVar(i, checkAliasing);
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 
 inline Value &
 AbstractFramePtr::unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing)
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->unaliasedLocal(i, checkAliasing);
 #ifdef JS_ION
-    return asBaselineFrame()->unaliasedLocal(i, checkAliasing);
+    if (isBaselineFrame())
+        return asBaselineFrame()->unaliasedLocal(i, checkAliasing);
+    return asRematerializedFrame()->unaliasedLocal(i, checkAliasing);
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 
 inline Value &
 AbstractFramePtr::unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing)
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->unaliasedFormal(i, checkAliasing);
 #ifdef JS_ION
-    return asBaselineFrame()->unaliasedFormal(i, checkAliasing);
+    if (isBaselineFrame())
+        return asBaselineFrame()->unaliasedFormal(i, checkAliasing);
+    return asRematerializedFrame()->unaliasedFormal(i, checkAliasing);
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 
 inline Value &
 AbstractFramePtr::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing)
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->unaliasedActual(i, checkAliasing);
 #ifdef JS_ION
-    return asBaselineFrame()->unaliasedActual(i, checkAliasing);
+    if (isBaselineFrame())
+        return asBaselineFrame()->unaliasedActual(i, checkAliasing);
+    return asRematerializedFrame()->unaliasedActual(i, checkAliasing);
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 
 inline bool
 AbstractFramePtr::hasCallObj() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->hasCallObj();
 #ifdef JS_ION
-    return asBaselineFrame()->hasCallObj();
+    if (isBaselineFrame())
+        return asBaselineFrame()->hasCallObj();
+    return asRematerializedFrame()->hasCallObj();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline bool
 AbstractFramePtr::useNewType() const
 {
     if (isInterpreterFrame())
@@ -564,182 +583,216 @@ AbstractFramePtr::isYielding() const
     return false;
 }
 inline bool
 AbstractFramePtr::isFunctionFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isFunctionFrame();
 #ifdef JS_ION
-    return asBaselineFrame()->isFunctionFrame();
+    if (isBaselineFrame())
+        return asBaselineFrame()->isFunctionFrame();
+    return asRematerializedFrame()->isFunctionFrame();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline bool
 AbstractFramePtr::isGlobalFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isGlobalFrame();
 #ifdef JS_ION
-    return asBaselineFrame()->isGlobalFrame();
+    if (isBaselineFrame())
+        return asBaselineFrame()->isGlobalFrame();
+    return asRematerializedFrame()->isGlobalFrame();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline bool
 AbstractFramePtr::isEvalFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isEvalFrame();
 #ifdef JS_ION
-    return asBaselineFrame()->isEvalFrame();
+    if (isBaselineFrame())
+        return asBaselineFrame()->isEvalFrame();
+    MOZ_ASSERT(isRematerializedFrame());
+    return false;
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline bool
 AbstractFramePtr::isFramePushedByExecute() const
 {
     return isGlobalFrame() || isEvalFrame();
 }
 inline bool
 AbstractFramePtr::isDebuggerFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isDebuggerFrame();
 #ifdef JS_ION
-    return asBaselineFrame()->isDebuggerFrame();
+    if (isBaselineFrame())
+        return asBaselineFrame()->isDebuggerFrame();
+    MOZ_ASSERT(isRematerializedFrame());
+    return false;
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline bool
 AbstractFramePtr::hasArgs() const {
     return isNonEvalFunctionFrame();
 }
 inline JSScript *
 AbstractFramePtr::script() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->script();
 #ifdef JS_ION
-    return asBaselineFrame()->script();
+    if (isBaselineFrame())
+        return asBaselineFrame()->script();
+    return asRematerializedFrame()->script();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline JSFunction *
 AbstractFramePtr::fun() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->fun();
 #ifdef JS_ION
-    return asBaselineFrame()->fun();
+    if (isBaselineFrame())
+        return asBaselineFrame()->fun();
+    return asRematerializedFrame()->fun();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline JSFunction *
 AbstractFramePtr::maybeFun() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->maybeFun();
 #ifdef JS_ION
-    return asBaselineFrame()->maybeFun();
+    if (isBaselineFrame())
+        return asBaselineFrame()->maybeFun();
+    return asRematerializedFrame()->maybeFun();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline JSFunction *
 AbstractFramePtr::callee() const
 {
     if (isInterpreterFrame())
         return &asInterpreterFrame()->callee();
 #ifdef JS_ION
-    return asBaselineFrame()->callee();
+    if (isBaselineFrame())
+        return asBaselineFrame()->callee();
+    return asRematerializedFrame()->callee();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline Value
 AbstractFramePtr::calleev() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->calleev();
 #ifdef JS_ION
-    return asBaselineFrame()->calleev();
+    if (isBaselineFrame())
+        return asBaselineFrame()->calleev();
+    return asRematerializedFrame()->calleev();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline bool
 AbstractFramePtr::isNonEvalFunctionFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isNonEvalFunctionFrame();
 #ifdef JS_ION
-    return asBaselineFrame()->isNonEvalFunctionFrame();
+    if (isBaselineFrame())
+        return asBaselineFrame()->isNonEvalFunctionFrame();
+    return asRematerializedFrame()->isNonEvalFunctionFrame();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline bool
 AbstractFramePtr::isNonStrictDirectEvalFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isNonStrictDirectEvalFrame();
 #ifdef JS_ION
-    return asBaselineFrame()->isNonStrictDirectEvalFrame();
+    if (isBaselineFrame())
+        return asBaselineFrame()->isNonStrictDirectEvalFrame();
+    MOZ_ASSERT(isRematerializedFrame());
+    return false;
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline bool
 AbstractFramePtr::isStrictEvalFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isStrictEvalFrame();
 #ifdef JS_ION
-    return asBaselineFrame()->isStrictEvalFrame();
+    if (isBaselineFrame())
+        return asBaselineFrame()->isStrictEvalFrame();
+    MOZ_ASSERT(isRematerializedFrame());
+    return false;
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 
 inline Value *
 AbstractFramePtr::argv() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->argv();
 #ifdef JS_ION
-    return asBaselineFrame()->argv();
+    if (isBaselineFrame())
+        return asBaselineFrame()->argv();
+    return asRematerializedFrame()->argv();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 
 inline bool
 AbstractFramePtr::hasArgsObj() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->hasArgsObj();
 #ifdef JS_ION
-    return asBaselineFrame()->hasArgsObj();
+    if (isBaselineFrame())
+        return asBaselineFrame()->hasArgsObj();
+    return asRematerializedFrame()->hasArgsObj();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline ArgumentsObject &
 AbstractFramePtr::argsObj() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->argsObj();
 #ifdef JS_ION
-    return asBaselineFrame()->argsObj();
+    if (isBaselineFrame())
+        return asBaselineFrame()->argsObj();
+    return asRematerializedFrame()->argsObj();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 inline void
 AbstractFramePtr::initArgsObj(ArgumentsObject &argsobj) const
 {
     if (isInterpreterFrame()) {
@@ -790,17 +843,19 @@ AbstractFramePtr::setPrevUpToDate() cons
 }
 
 inline Value &
 AbstractFramePtr::thisValue() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->thisValue();
 #ifdef JS_ION
-    return asBaselineFrame()->thisValue();
+    if (isBaselineFrame())
+        return asBaselineFrame()->thisValue();
+    return asRematerializedFrame()->thisValue();
 #else
     MOZ_ASSUME_UNREACHABLE("Invalid frame");
 #endif
 }
 
 inline void
 AbstractFramePtr::popBlock(JSContext *cx) const
 {
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -628,31 +628,33 @@ FrameIter::Data::Data(JSContext *cx, Sav
     savedOption_(savedOption),
     contextOption_(contextOption),
     principals_(principals),
     pc_(nullptr),
     interpFrames_(nullptr),
     activations_(cx->runtime())
 #ifdef JS_ION
   , jitFrames_((uint8_t *)nullptr, SequentialExecution)
+  , ionInlineFrameNo_(0)
 #endif
 {
 }
 
 FrameIter::Data::Data(const FrameIter::Data &other)
   : cx_(other.cx_),
     savedOption_(other.savedOption_),
     contextOption_(other.contextOption_),
     principals_(other.principals_),
     state_(other.state_),
     pc_(other.pc_),
     interpFrames_(other.interpFrames_),
     activations_(other.activations_)
 #ifdef JS_ION
   , jitFrames_(other.jitFrames_)
+  , ionInlineFrameNo_(other.ionInlineFrameNo_)
 #endif
 {
 }
 
 FrameIter::FrameIter(JSContext *cx, SavedOption savedOption)
   : data_(cx, savedOption, CURRENT_CONTEXT, nullptr)
 #ifdef JS_ION
   , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
@@ -682,16 +684,21 @@ FrameIter::FrameIter(const FrameIter &ot
 
 FrameIter::FrameIter(const Data &data)
   : data_(data)
 #ifdef JS_ION
   , ionInlineFrames_(data.cx_, data_.jitFrames_.isIonJS() ? &data_.jitFrames_ : nullptr)
 #endif
 {
     JS_ASSERT(data.cx_);
+
+    if (data_.jitFrames_.isIonJS()) {
+        while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_)
+            ++ionInlineFrames_;
+    }
 }
 
 #ifdef JS_ION
 void
 FrameIter::nextJitFrame()
 {
     if (data_.jitFrames_.isIonJS()) {
         ionInlineFrames_.resetOn(&data_.jitFrames_);
@@ -730,16 +737,17 @@ FrameIter &
 FrameIter::operator++()
 {
     switch (data_.state_) {
       case DONE:
         MOZ_ASSUME_UNREACHABLE("Unexpected state");
       case INTERP:
         if (interpFrame()->isDebuggerFrame() && interpFrame()->evalInFramePrev()) {
             AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev();
+            MOZ_ASSERT(!eifPrev.isRematerializedFrame());
 
             // Eval-in-frame can cross contexts and works across saved frame
             // chains.
             ContextOption prevContextOption = data_.contextOption_;
             SavedOption prevSavedOption = data_.savedOption_;
             data_.contextOption_ = ALL_CONTEXTS;
             data_.savedOption_ = GO_THROUGH_SAVED;
 
@@ -780,25 +788,23 @@ FrameIter::operator++()
         break;
     }
     return *this;
 }
 
 FrameIter::Data *
 FrameIter::copyData() const
 {
+    Data *data = data_.cx_->new_<Data>(data_);
 #ifdef JS_ION
-    /*
-     * This doesn't work for optimized Ion frames since ionInlineFrames_ is
-     * not copied.
-     */
     JS_ASSERT(data_.state_ != ASMJS);
-    JS_ASSERT(data_.jitFrames_.type() != jit::JitFrame_IonJS);
+    if (data && data_.jitFrames_.isIonJS())
+        data->ionInlineFrameNo_ = ionInlineFrames_.frameNo();
 #endif
-    return data_.cx_->new_<Data>(data_);
+    return data;
 }
 
 AbstractFramePtr
 FrameIter::copyDataAsAbstractFramePtr() const
 {
     AbstractFramePtr frame;
     if (Data *data = copyData())
         frame.ptr_ = uintptr_t(data);
@@ -1055,22 +1061,30 @@ FrameIter::isConstructing() const
 
 AbstractFramePtr
 FrameIter::abstractFramePtr() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
-      case JIT:
+      case JIT: {
 #ifdef JS_ION
         if (data_.jitFrames_.isBaselineJS())
             return data_.jitFrames_.baselineFrame();
+
+        MOZ_ASSERT(data_.jitFrames_.isIonJS());
+        jit::RematerializedFrame *frame =
+            activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
+                                                             ionInlineFrames_.frameNo());
+        MOZ_ASSERT(frame);
+        return frame;
 #endif
         break;
+      }
       case INTERP:
         JS_ASSERT(interpFrame());
         return AbstractFramePtr(interpFrame());
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
 
 void
@@ -1193,31 +1207,17 @@ unsigned
 FrameIter::numFormalArgs() const
 {
     return script()->functionNonDelazifying()->nargs();
 }
 
 Value
 FrameIter::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const
 {
-    switch (data_.state_) {
-      case DONE:
-      case ASMJS:
-        break;
-      case INTERP:
-        return interpFrame()->unaliasedActual(i, checkAliasing);
-      case JIT:
-#ifdef JS_ION
-        JS_ASSERT(data_.jitFrames_.isBaselineJS());
-        return data_.jitFrames_.baselineFrame()->unaliasedActual(i, checkAliasing);
-#else
-        break;
-#endif
-    }
-    MOZ_ASSUME_UNREACHABLE("Unexpected state");
+    return abstractFramePtr().unaliasedActual(i, checkAliasing);
 }
 
 JSObject *
 FrameIter::scopeChain() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
@@ -1245,77 +1245,51 @@ FrameIter::callObj() const
     while (!pobj->is<CallObject>())
         pobj = pobj->enclosingScope();
     return pobj->as<CallObject>();
 }
 
 bool
 FrameIter::hasArgsObj() const
 {
-    switch (data_.state_) {
-      case DONE:
-      case ASMJS:
-        break;
-      case INTERP:
-        return interpFrame()->hasArgsObj();
-      case JIT:
-#ifdef JS_ION
-        JS_ASSERT(data_.jitFrames_.isBaselineJS());
-        return data_.jitFrames_.baselineFrame()->hasArgsObj();
-#else
-        break;
-#endif
-    }
-    MOZ_ASSUME_UNREACHABLE("Unexpected state");
+    return abstractFramePtr().hasArgsObj();
 }
 
 ArgumentsObject &
 FrameIter::argsObj() const
 {
-    JS_ASSERT(hasArgsObj());
-
-    switch (data_.state_) {
-      case DONE:
-      case ASMJS:
-        break;
-      case JIT:
-#ifdef JS_ION
-        JS_ASSERT(data_.jitFrames_.isBaselineJS());
-        return data_.jitFrames_.baselineFrame()->argsObj();
-#else
-        break;
-#endif
-      case INTERP:
-        return interpFrame()->argsObj();
-    }
-    MOZ_ASSUME_UNREACHABLE("Unexpected state");
+    MOZ_ASSERT(hasArgsObj());
+    return abstractFramePtr().argsObj();
 }
 
 bool
 FrameIter::computeThis(JSContext *cx) const
 {
     JS_ASSERT(!done() && !isAsmJS());
-    if (!isIon()) {
-        assertSameCompartment(cx, scopeChain());
-        return ComputeThis(cx, abstractFramePtr());
-    }
-    return true;
+    assertSameCompartment(cx, scopeChain());
+    return ComputeThis(cx, abstractFramePtr());
+}
+
+Value
+FrameIter::computedThisValue() const
+{
+    return abstractFramePtr().thisValue();
 }
 
 Value
 FrameIter::thisv() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
 #ifdef JS_ION
         if (data_.jitFrames_.isIonJS())
-            return ObjectValue(*ionInlineFrames_.thisObject());
+            return ionInlineFrames_.thisValue();
         return data_.jitFrames_.baselineFrame()->thisValue();
 #else
         break;
 #endif
       case INTERP:
         return interpFrame()->thisValue();
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
@@ -1492,16 +1466,19 @@ js::CheckLocalUnaliased(MaybeCheckAliasi
     }
 }
 #endif
 
 jit::JitActivation::JitActivation(JSContext *cx, bool firstFrameIsConstructing, bool active)
   : Activation(cx, Jit),
     firstFrameIsConstructing_(firstFrameIsConstructing),
     active_(active)
+#ifdef JS_ION
+  , rematerializedFrames_(cx)
+#endif
 {
     if (active) {
         prevIonTop_ = cx->mainThread().ionTop;
         prevJitJSContext_ = cx->mainThread().jitJSContext;
         cx->mainThread().jitJSContext = cx;
     } else {
         prevIonTop_ = nullptr;
         prevJitJSContext_ = nullptr;
@@ -1509,16 +1486,20 @@ jit::JitActivation::JitActivation(JSCont
 }
 
 jit::JitActivation::~JitActivation()
 {
     if (active_) {
         cx_->mainThread().ionTop = prevIonTop_;
         cx_->mainThread().jitJSContext = prevJitJSContext_;
     }
+
+#ifdef JS_ION
+    clearRematerializedFrames();
+#endif
 }
 
 // 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)
 {
@@ -1533,16 +1514,119 @@ jit::JitActivation::setActive(JSContext 
         prevJitJSContext_ = cx->mainThread().jitJSContext;
         cx->mainThread().jitJSContext = cx;
     } else {
         cx->mainThread().ionTop = prevIonTop_;
         cx->mainThread().jitJSContext = prevJitJSContext_;
     }
 }
 
+#ifdef JS_ION
+
+void
+jit::JitActivation::freeRematerializedFramesInVector(RematerializedFrameVector &frames)
+{
+    for (size_t i = 0; i < frames.length(); i++) {
+        RematerializedFrame *f = frames[i];
+        f->RematerializedFrame::~RematerializedFrame();
+        js_free(f);
+    }
+    frames.clear();
+}
+
+void
+jit::JitActivation::removeRematerializedFrame(uint8_t *top)
+{
+    if (!rematerializedFrames_.initialized())
+        return;
+
+    if (RematerializedFrameTable::Ptr p = rematerializedFrames_.lookup(top)) {
+        freeRematerializedFramesInVector(p->value());
+        rematerializedFrames_.remove(p);
+    }
+}
+
+void
+jit::JitActivation::clearRematerializedFrames()
+{
+    if (!rematerializedFrames_.initialized())
+        return;
+
+    for (RematerializedFrameTable::Enum e(rematerializedFrames_); !e.empty(); e.popFront()) {
+        freeRematerializedFramesInVector(e.front().value());
+        e.removeFront();
+    }
+}
+
+jit::RematerializedFrame *
+jit::JitActivation::getRematerializedFrame(JSContext *cx, JitFrameIterator &iter,
+                                           size_t inlineDepth)
+{
+    MOZ_ASSERT(iter.activation() == this);
+    MOZ_ASSERT(iter.isIonJS());
+
+    if (!rematerializedFrames_.initialized() && !rematerializedFrames_.init())
+        return nullptr;
+
+    // The unit of rematerialization is an uninlined frame and its inlined
+    // frames. Since inlined frames do not exist outside of snapshots, it is
+    // impossible to synchronize their rematerialized copies to preserve
+    // identity. Therefore, we always rematerialize an uninlined frame and all
+    // its inlined frames at once.
+
+    uint8_t *top = iter.fp();
+    RematerializedFrameTable::AddPtr p = rematerializedFrames_.lookupForAdd(top);
+    if (!p) {
+        RematerializedFrameVector empty(cx);
+        if (!rematerializedFrames_.add(p, top, Move(empty)))
+            return nullptr;
+
+        InlineFrameIterator inlineIter(cx, &iter);
+        if (!p->value().resize(inlineIter.frameCount()))
+            return nullptr;
+
+        while (true) {
+            size_t frameNo = inlineIter.frameNo();
+            p->value()[frameNo] = RematerializedFrame::New(cx, top, inlineIter);
+            if (!p->value()[frameNo])
+                return nullptr;
+
+            if (!inlineIter.more())
+                break;
+            ++inlineIter;
+        }
+    }
+
+    return p->value()[inlineDepth];
+}
+
+jit::RematerializedFrame *
+jit::JitActivation::lookupRematerializedFrame(uint8_t *top, size_t inlineDepth)
+{
+    if (!rematerializedFrames_.initialized())
+        return nullptr;
+    if (RematerializedFrameTable::Ptr p = rematerializedFrames_.lookup(top))
+        return inlineDepth < p->value().length() ? p->value()[inlineDepth] : nullptr;
+    return nullptr;
+}
+
+void
+jit::JitActivation::markRematerializedFrames(JSTracer *trc)
+{
+    if (!rematerializedFrames_.initialized())
+        return;
+    for (RematerializedFrameTable::Enum e(rematerializedFrames_); !e.empty(); e.popFront()) {
+        RematerializedFrameVector &frames = e.front().value();
+        for (size_t i = 0; i < frames.length(); i++)
+            frames[i]->mark(trc);
+    }
+}
+
+#endif // JS_ION
+
 AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module, unsigned exportIndex)
   : Activation(cx, AsmJS),
     module_(module),
     errorRejoinSP_(nullptr),
     profiler_(nullptr),
     resumePC_(nullptr),
     exportIndex_(exportIndex)
 {
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -70,16 +70,17 @@ enum MaybeCheckAliasing { CHECK_ALIASING
 
 #ifdef DEBUG
 extern void
 CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, uint32_t i);
 #endif
 
 namespace jit {
     class BaselineFrame;
+    class RematerializedFrame;
 }
 
 /*
  * Pointer to either a ScriptFrameIter::Data, an InterpreterFrame, or a Baseline
  * JIT frame.
  *
  * The Debugger may cache ScriptFrameIter::Data as a bookmark to reconstruct a
  * ScriptFrameIter without doing a full stack walk.
@@ -101,34 +102,41 @@ class AbstractFramePtr
     friend class FrameIter;
 
     uintptr_t ptr_;
 
     enum {
         Tag_ScriptFrameIterData = 0x0,
         Tag_InterpreterFrame = 0x1,
         Tag_BaselineFrame = 0x2,
+        Tag_RematerializedFrame = 0x3,
         TagMask = 0x3
     };
 
   public:
     AbstractFramePtr()
       : ptr_(0)
     {}
 
     AbstractFramePtr(InterpreterFrame *fp)
       : ptr_(fp ? uintptr_t(fp) | Tag_InterpreterFrame : 0)
     {
-        MOZ_ASSERT(asInterpreterFrame() == fp);
+        MOZ_ASSERT_IF(fp, asInterpreterFrame() == fp);
     }
 
     AbstractFramePtr(jit::BaselineFrame *fp)
       : ptr_(fp ? uintptr_t(fp) | Tag_BaselineFrame : 0)
     {
-        MOZ_ASSERT(asBaselineFrame() == fp);
+        MOZ_ASSERT_IF(fp, asBaselineFrame() == fp);
+    }
+
+    AbstractFramePtr(jit::RematerializedFrame *fp)
+      : ptr_(fp ? uintptr_t(fp) | Tag_RematerializedFrame : 0)
+    {
+        MOZ_ASSERT_IF(fp, asRematerializedFrame() == fp);
     }
 
     explicit AbstractFramePtr(JSAbstractFramePtr frame)
         : ptr_(uintptr_t(frame.raw()))
     {
     }
 
     static AbstractFramePtr FromRaw(void *raw) {
@@ -136,33 +144,42 @@ class AbstractFramePtr
         frame.ptr_ = uintptr_t(raw);
         return frame;
     }
 
     bool isScriptFrameIterData() const {
         return !!ptr_ && (ptr_ & TagMask) == Tag_ScriptFrameIterData;
     }
     bool isInterpreterFrame() const {
-        return ptr_ & Tag_InterpreterFrame;
+        return (ptr_ & TagMask) == Tag_InterpreterFrame;
     }
     InterpreterFrame *asInterpreterFrame() const {
         JS_ASSERT(isInterpreterFrame());
         InterpreterFrame *res = (InterpreterFrame *)(ptr_ & ~TagMask);
         JS_ASSERT(res);
         return res;
     }
     bool isBaselineFrame() const {
-        return ptr_ & Tag_BaselineFrame;
+        return (ptr_ & TagMask) == Tag_BaselineFrame;
     }
     jit::BaselineFrame *asBaselineFrame() const {
         JS_ASSERT(isBaselineFrame());
         jit::BaselineFrame *res = (jit::BaselineFrame *)(ptr_ & ~TagMask);
         JS_ASSERT(res);
         return res;
     }
+    bool isRematerializedFrame() const {
+        return (ptr_ & TagMask) == Tag_RematerializedFrame;
+    }
+    jit::RematerializedFrame *asRematerializedFrame() const {
+        JS_ASSERT(isRematerializedFrame());
+        jit::RematerializedFrame *res = (jit::RematerializedFrame *)(ptr_ & ~TagMask);
+        JS_ASSERT(res);
+        return res;
+    }
 
     void *raw() const { return reinterpret_cast<void *>(ptr_); }
 
     bool operator ==(const AbstractFramePtr &other) const { return ptr_ == other.ptr_; }
     bool operator !=(const AbstractFramePtr &other) const { return ptr_ != other.ptr_; }
 
     operator bool() const { return !!ptr_; }
 
@@ -1304,16 +1321,30 @@ namespace jit {
 // A JitActivation is used for frames running in Baseline or Ion.
 class JitActivation : public Activation
 {
     uint8_t *prevIonTop_;
     JSContext *prevJitJSContext_;
     bool firstFrameIsConstructing_;
     bool active_;
 
+#ifdef JS_ION
+    // Rematerialized Ion frames which has info copied out of snapshots. Maps
+    // frame pointers (i.e. ionTop) to a vector of rematerializations of all
+    // inline frames associated with that frame.
+    //
+    // This table is lazily initialized by calling getRematerializedFrame.
+    typedef Vector<RematerializedFrame *, 1> RematerializedFrameVector;
+    typedef HashMap<uint8_t *, RematerializedFrameVector> RematerializedFrameTable;
+    RematerializedFrameTable rematerializedFrames_;
+
+    void freeRematerializedFramesInVector(RematerializedFrameVector &frames);
+    void clearRematerializedFrames();
+#endif
+
 #ifdef CHECK_OSIPOINT_REGISTERS
   protected:
     // Used to verify that live registers don't change between a VM call and
     // the OsiPoint that follows it. Protected to silence Clang warning.
     uint32_t checkRegs_;
     RegisterDump regs_;
 #endif
 
@@ -1352,16 +1383,35 @@ class JitActivation : public Activation
     }
     static size_t offsetOfCheckRegs() {
         return offsetof(JitActivation, checkRegs_);
     }
     static size_t offsetOfRegs() {
         return offsetof(JitActivation, regs_);
     }
 #endif
+
+#ifdef JS_ION
+    // Look up a rematerialized frame keyed by the fp, rematerializing the
+    // frame if one doesn't already exist. A frame can only be rematerialized
+    // if an IonFrameIterator pointing to the nearest uninlined frame can be
+    // provided, as values need to be read out of snapshots.
+    //
+    // The inlineDepth must be within bounds of the frame pointed to by iter.
+    RematerializedFrame *getRematerializedFrame(JSContext *cx, JitFrameIterator &iter,
+                                                size_t inlineDepth = 0);
+
+    // Look up a rematerialized frame by the fp.
+    RematerializedFrame *lookupRematerializedFrame(uint8_t *top, size_t inlineDepth = 0);
+
+    // Remove a previous rematerialization by fp.
+    void removeRematerializedFrame(uint8_t *top);
+
+    void markRematerializedFrames(JSTracer *trc);
+#endif
 };
 
 // A filtering of the ActivationIterator to only stop at JitActivations.
 class JitActivationIterator : public ActivationIterator
 {
     void settle() {
         while (!done() && !activation_->isJit())
             ActivationIterator::operator++();
@@ -1511,16 +1561,17 @@ class FrameIter
 
         jsbytecode *    pc_;
 
         InterpreterFrameIterator interpFrames_;
         ActivationIterator activations_;
 
 #ifdef JS_ION
         jit::JitFrameIterator jitFrames_;
+        unsigned ionInlineFrameNo_;
 #endif
 
         Data(JSContext *cx, SavedOption savedOption, ContextOption contextOption,
              JSPrincipals *principals);
         Data(const Data &other);
     };
 
     FrameIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
@@ -1548,16 +1599,20 @@ class FrameIter
 
     bool isFunctionFrame() const;
     bool isGlobalFrame() const;
     bool isEvalFrame() const;
     bool isNonEvalFunctionFrame() const;
     bool isGeneratorFrame() const;
     bool hasArgs() const { return isNonEvalFunctionFrame(); }
 
+    /*
+     * Get an abstract frame pointer dispatching to either an interpreter,
+     * baseline, or rematerialized optimized frame.
+     */
     ScriptSource *scriptSource() const;
     const char *scriptFilename() const;
     unsigned computeLine(uint32_t *column = nullptr) const;
     JSAtom *functionDisplayAtom() const;
     JSPrincipals *originPrincipals() const;
 
     bool hasScript() const { return !isAsmJS(); }
 
@@ -1578,18 +1633,26 @@ class FrameIter
     template <class Op> inline void unaliasedForEachActual(JSContext *cx, Op op);
 
     JSObject   *scopeChain() const;
     CallObject &callObj() const;
 
     bool        hasArgsObj() const;
     ArgumentsObject &argsObj() const;
 
-    // Ensure that thisv is correct, see ComputeThis.
+    // Ensure that computedThisValue is correct, see ComputeThis.
     bool        computeThis(JSContext *cx) const;
+    // thisv() may not always be correct, even after computeThis. In case when
+    // the frame is an Ion frame, the computed this value cannot be saved to
+    // the Ion frame but is instead saved in the RematerializedFrame for use
+    // by Debugger.
+    //
+    // Both methods exist because of speed. thisv() will never rematerialize
+    // an Ion frame, whereas computedThisValue() will.
+    Value       computedThisValue() const;
     Value       thisv() const;
 
     Value       returnValue() const;
     void        setReturnValue(const Value &v);
 
     JSFunction *maybeCallee() const {
         return isFunctionFrame() ? callee() : nullptr;
     }