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 199507 061ebab47be320047966424d449c90de296ea930
parent 199506 b30afb9de404554b547a9adba6bc483fd045a584
child 199508 a19a7c0a4b04602d66c3ecf3c956a6cee8ba6559
push id486
push userasasaki@mozilla.com
push dateMon, 14 Jul 2014 18:39:42 +0000
treeherdermozilla-release@d33428174ff1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs716647
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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;
     }