Bug 1286948 - Extends AbstractFramePtr to reference wasm::DebugFrame. r=luke,shu
authorYury Delendik <ydelendik@mozilla.com>
Sat, 07 Jan 2017 10:36:11 -0600
changeset 328610 766ead4652099ace9e0622009f6d404e89a78f68
parent 328609 4762c110448a11b0eadbf8f2b523f5729d61db71
child 328611 01f686c12291fba93fcbdd7d55454e4cbe941f14
push id31181
push usercbook@mozilla.com
push dateTue, 10 Jan 2017 11:09:50 +0000
treeherdermozilla-central@acd4177b28d7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke, shu
bugs1286948
milestone53.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 1286948 - Extends AbstractFramePtr to reference wasm::DebugFrame. r=luke,shu Debugger.cpp use FrameIter to walk the stack. Having extended AbstractFramePtr helps with simulating wasm callstack as JS one. MozReview-Commit-ID: 7zGWZp33sme
js/src/gdb/mozilla/Interpreter.py
js/src/gdb/tests/test-Interpreter.cpp
js/src/jit/shared/Assembler-shared.h
js/src/moz.build
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/EnvironmentObject.cpp
js/src/vm/EnvironmentObject.h
js/src/vm/SavedStacks.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmDebugFrame.cpp
js/src/wasm/WasmDebugFrame.h
js/src/wasm/WasmFrameIterator.cpp
js/src/wasm/WasmFrameIterator.h
js/src/wasm/WasmTypes.h
--- a/js/src/gdb/mozilla/Interpreter.py
+++ b/js/src/gdb/mozilla/Interpreter.py
@@ -11,16 +11,17 @@ from mozilla.prettyprinters import prett
 class InterpreterTypeCache(object):
     def __init__(self):
         self.tValue = gdb.lookup_type('JS::Value')
         self.tJSOp = gdb.lookup_type('JSOp')
         self.tScriptFrameIterData = gdb.lookup_type('js::ScriptFrameIter::Data')
         self.tInterpreterFrame = gdb.lookup_type('js::InterpreterFrame')
         self.tBaselineFrame = gdb.lookup_type('js::jit::BaselineFrame')
         self.tRematerializedFrame = gdb.lookup_type('js::jit::RematerializedFrame')
+        self.tDebugFrame = gdb.lookup_type('js::wasm::DebugFrame')
 
 @pretty_printer('js::InterpreterRegs')
 class InterpreterRegs(object):
     def __init__(self, value, cache):
         self.value = value
         self.cache = cache
         if not cache.mod_Interpreter:
             cache.mod_Interpreter = InterpreterTypeCache()
@@ -42,17 +43,18 @@ class InterpreterRegs(object):
         return '{{ {}, {}, {} }}'.format(fp_, sp, pc)
 
 @pretty_printer('js::AbstractFramePtr')
 class AbstractFramePtr(object):
     Tag_ScriptFrameIterData = 0x0
     Tag_InterpreterFrame = 0x1
     Tag_BaselineFrame = 0x2
     Tag_RematerializedFrame = 0x3
-    TagMask = 0x3
+    Tag_WasmDebugFrame = 0x4
+    TagMask = 0x7
 
     def __init__(self, value, cache):
         self.value = value
         self.cache = cache
         if not cache.mod_Interpreter:
             cache.mod_Interpreter = InterpreterTypeCache()
         self.itc = cache.mod_Interpreter
 
@@ -67,14 +69,17 @@ class AbstractFramePtr(object):
             label = 'js::InterpreterFrame'
             ptr = ptr.cast(self.itc.tInterpreterFrame.pointer())
         if tag == AbstractFramePtr.Tag_BaselineFrame:
             label = 'js::jit::BaselineFrame'
             ptr = ptr.cast(self.itc.tBaselineFrame.pointer())
         if tag == AbstractFramePtr.Tag_RematerializedFrame:
             label = 'js::jit::RematerializedFrame'
             ptr = ptr.cast(self.itc.tRematerializedFrame.pointer())
+        if tag == AbstractFramePtr.Tag_WasmDebugFrame:
+            label = 'js::wasm::DebugFrame'
+            ptr = ptr.cast(self.itc.tDebugFrame.pointer())
         return 'AbstractFramePtr (({} *) {})'.format(label, ptr)
 
     # Provide the ptr_ field as a child, so it prints after the pretty string
     # provided above.
     def children(self):
         yield ('ptr_', self.value['ptr_'])
--- a/js/src/gdb/tests/test-Interpreter.cpp
+++ b/js/src/gdb/tests/test-Interpreter.cpp
@@ -45,16 +45,23 @@ GDBTestInitAbstractFramePtr(AbstractFram
 
 void
 GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, jit::RematerializedFrame* ptr)
 {
     MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
     frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_RematerializedFrame;
 }
 
+void
+GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, wasm::DebugFrame* ptr)
+{
+    MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
+    frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_WasmDebugFrame;
+}
+
 } // namespace js
 
 FRAGMENT(Interpreter, Regs) {
   struct FakeFrame {
     js::InterpreterFrame frame;
     JS::Value slot0;
     JS::Value slot1;
     JS::Value slot2;
@@ -78,15 +85,18 @@ FRAGMENT(Interpreter, AbstractFramePtr) 
     GDBTestInitAbstractFramePtr(ifptr, (js::InterpreterFrame*) uintptr_t(0x8badf00));
 
     js::AbstractFramePtr bfptr;
     GDBTestInitAbstractFramePtr(bfptr, (js::jit::BaselineFrame*) uintptr_t(0xbadcafe0));
 
     js::AbstractFramePtr rfptr;
     GDBTestInitAbstractFramePtr(rfptr, (js::jit::RematerializedFrame*) uintptr_t(0xdabbad00));
 
+    js::AbstractFramePtr sfptr;
+    GDBTestInitAbstractFramePtr(sfptr, (js::wasm::DebugFrame*) uintptr_t(0xcb98ad00));
+
     breakpoint();
 
     (void) sfidptr;
     (void) ifptr;
     (void) bfptr;
     (void) rfptr;
 }
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -660,38 +660,16 @@ class CodeLocationLabel
         return raw_;
     }
 };
 
 } // namespace jit
 
 namespace wasm {
 
-// As an invariant across architectures, within wasm code:
-//   $sp % WasmStackAlignment = (sizeof(wasm::Frame) + masm.framePushed) % WasmStackAlignment
-// Thus, wasm::Frame represents the bytes pushed after the call (which occurred
-// with a WasmStackAlignment-aligned StackPointer) that are not included in
-// masm.framePushed.
-
-struct Frame
-{
-    // The caller's saved frame pointer. In non-profiling mode, internal
-    // wasm-to-wasm calls don't update fp and thus don't save the caller's
-    // frame pointer; the space is reserved, however, so that profiling mode can
-    // reuse the same function body without recompiling.
-    uint8_t* callerFP;
-
-    // The return address pushed by the call (in the case of ARM/MIPS the return
-    // address is pushed by the first instruction of the prologue).
-    void* returnAddress;
-};
-
-static_assert(sizeof(Frame) == 2 * sizeof(void*), "?!");
-static const uint32_t FrameBytesAfterReturnAddress = sizeof(void*);
-
 // Represents an instruction to be patched and the intended pointee. These
 // links are accumulated in the MacroAssembler, but patching is done outside
 // the MacroAssembler (in Module::staticallyLink).
 
 struct SymbolicAccess
 {
     SymbolicAccess(jit::CodeOffset patchAt, SymbolicAddress target)
       : patchAt(patchAt), target(target) {}
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -368,16 +368,17 @@ UNIFIED_SOURCES += [
     'wasm/WasmBaselineCompile.cpp',
     'wasm/WasmBinaryIterator.cpp',
     'wasm/WasmBinaryToAST.cpp',
     'wasm/WasmBinaryToExperimentalText.cpp',
     'wasm/WasmBinaryToText.cpp',
     'wasm/WasmCode.cpp',
     'wasm/WasmCompartment.cpp',
     'wasm/WasmCompile.cpp',
+    'wasm/WasmDebugFrame.cpp',
     'wasm/WasmFrameIterator.cpp',
     'wasm/WasmGenerator.cpp',
     'wasm/WasmInstance.cpp',
     'wasm/WasmIonCompile.cpp',
     'wasm/WasmJS.cpp',
     'wasm/WasmModule.cpp',
     'wasm/WasmSignalHandlers.cpp',
     'wasm/WasmStubs.cpp',
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2044,16 +2044,18 @@ Debugger::onSingleStep(JSContext* cx, Mu
         JSScript* trappingScript = iter.script();
         GlobalObject* global = cx->global();
         if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
             for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
                 Debugger* dbg = *p;
                 for (FrameMap::Range r = dbg->frames.all(); !r.empty(); r.popFront()) {
                     AbstractFramePtr frame = r.front().key();
                     NativeObject* frameobj = r.front().value();
+                    if (frame.isWasmDebugFrame())
+                        continue;
                     if (frame.script() == trappingScript &&
                         !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
                     {
                         stepperCount++;
                     }
                 }
             }
         }
@@ -6256,43 +6258,54 @@ DebuggerScript_getLineOffsets(JSContext*
 
     args.rval().setObject(*matcher.result());
     return true;
 }
 
 bool
 Debugger::observesFrame(AbstractFramePtr frame) const
 {
+    if (frame.isWasmDebugFrame())
+        return observesWasm(frame.wasmInstance());
+
     return observesScript(frame.script());
 }
 
 bool
 Debugger::observesFrame(const FrameIter& iter) const
 {
     // Skip frames not yet fully initialized during their prologue.
     if (iter.isInterp() && iter.isFunctionFrame()) {
         const Value& thisVal = iter.interpFrame()->thisArgument();
         if (thisVal.isMagic() && thisVal.whyMagic() == JS_IS_CONSTRUCTING)
             return false;
     }
     if (iter.isWasm())
-        return false;
+        return observesWasm(iter.wasmInstance());
     return observesScript(iter.script());
 }
 
 bool
 Debugger::observesScript(JSScript* script) const
 {
     if (!enabled)
         return false;
     // Don't ever observe self-hosted scripts: the Debugger API can break
     // self-hosted invariants.
     return observesGlobal(&script->global()) && !script->selfHosted();
 }
 
+bool
+Debugger::observesWasm(wasm::Instance* instance) const
+{
+    if (!enabled || !instance->code().metadata().debugEnabled)
+        return false;
+    return false; // TODO check global
+}
+
 /* static */ bool
 Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to,
                            ScriptFrameIter& iter)
 {
     auto removeFromDebuggerFramesOnExit = MakeScopeExit([&] {
         // Remove any remaining old entries on exit, as the 'from' frame will
         // be gone. This is only done in the failure case. On failure, the
         // removeToDebuggerFramesOnExit lambda below will rollback any frames
@@ -7370,16 +7383,21 @@ DebuggerFrame::getIsConstructing(JSConte
 
     result = iter.isFunctionFrame() && iter.isConstructing();
     return true;
 }
 
 static void
 UpdateFrameIterPc(FrameIter& iter)
 {
+    if (iter.abstractFramePtr().isWasmDebugFrame()) {
+        // Wasm debug frames don't need their pc updated -- it's null.
+        return;
+    }
+
     if (iter.abstractFramePtr().isRematerializedFrame()) {
 #ifdef DEBUG
         // Rematerialized frames don't need their pc updated. The reason we
         // need to update pc is because we might get the same Debugger.Frame
         // object for multiple re-entries into debugger code from debuggee
         // code. This reentrancy is not possible with rematerialized frames,
         // because when returning to debuggee code, we would have bailed out
         // to baseline.
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -912,16 +912,17 @@ class Debugger : private mozilla::Linked
 
     inline bool observesEnterFrame() const;
     inline bool observesNewScript() const;
     inline bool observesNewGlobalObject() const;
     inline bool observesGlobal(GlobalObject* global) const;
     bool observesFrame(AbstractFramePtr frame) const;
     bool observesFrame(const FrameIter& iter) const;
     bool observesScript(JSScript* script) const;
+    bool observesWasm(wasm::Instance* instance) const;
 
     /*
      * If env is nullptr, call vp->setNull() and return true. Otherwise, find
      * or create a Debugger.Environment object for the given Env. On success,
      * store the Environment object in *vp and return true.
      */
     MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env, MutableHandleValue vp);
     MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env,
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -17,16 +17,17 @@
 #include "frontend/ParseNode.h"
 #include "gc/Policy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/AsyncFunction.h"
 #include "vm/GlobalObject.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
 #include "vm/Xdr.h"
+#include "wasm/WasmInstance.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
@@ -1228,16 +1229,27 @@ EnvironmentIter::EnvironmentIter(JSConte
     env_(cx, frame.environmentChain()),
     frame_(frame)
 {
     assertSameCompartment(cx, frame);
     settle();
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
+EnvironmentIter::EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope, AbstractFramePtr frame
+                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+  : si_(cx, ScopeIter(scope)),
+    env_(cx, env),
+    frame_(frame)
+{
+    assertSameCompartment(cx, frame);
+    settle();
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+}
+
 void
 EnvironmentIter::incrementScopeIter()
 {
     if (si_.scope()->is<GlobalScope>()) {
         // GlobalScopes may be syntactic or non-syntactic. Non-syntactic
         // GlobalScopes correspond to zero or more non-syntactic
         // EnvironmentsObjects followed by the global lexical scope, then the
         // GlobalObject or another non-EnvironmentObject object.
@@ -1248,35 +1260,40 @@ EnvironmentIter::incrementScopeIter()
     }
 }
 
 void
 EnvironmentIter::settle()
 {
     // Check for trying to iterate a function or eval frame before the prologue has
     // created the CallObject, in which case we have to skip.
-    if (frame_ && frame_.script()->initialEnvironmentShape() && !frame_.hasInitialEnvironment()) {
+    if (frame_ && frame_.hasScript() &&
+        frame_.script()->initialEnvironmentShape() && !frame_.hasInitialEnvironment())
+    {
         // Skip until we're at the enclosing scope of the script.
         while (si_.scope() != frame_.script()->enclosingScope()) {
             if (env_->is<LexicalEnvironmentObject>() &&
                 !env_->as<LexicalEnvironmentObject>().isExtensible() &&
                 &env_->as<LexicalEnvironmentObject>().scope() == si_.scope())
             {
                 MOZ_ASSERT(si_.kind() == ScopeKind::NamedLambda ||
                            si_.kind() == ScopeKind::StrictNamedLambda);
                 env_ = &env_->as<EnvironmentObject>().enclosingEnvironment();
             }
             incrementScopeIter();
         }
     }
 
     // Check if we have left the extent of the initial frame after we've
     // settled on a static scope.
-    if (frame_ && (!si_ || si_.scope() == frame_.script()->enclosingScope()))
+    if (frame_ && (frame_.isWasmDebugFrame() ||
+                   (!si_ || si_.scope() == frame_.script()->enclosingScope())))
+    {
         frame_ = NullFramePtr();
+    }
 
 #ifdef DEBUG
     if (si_) {
         if (hasSyntacticEnvironment()) {
             Scope* scope = si_.scope();
             if (scope->is<LexicalScope>()) {
                 MOZ_ASSERT(scope == &env_->as<LexicalEnvironmentObject>().scope());
             } else if (scope->is<FunctionScope>()) {
@@ -2757,17 +2774,22 @@ DebugEnvironments::updateLiveEnvironment
             continue;
 
         if (frame.isFunctionFrame() && frame.callee()->isGenerator())
             continue;
 
         if (!frame.isDebuggee())
             continue;
 
-        for (EnvironmentIter ei(cx, frame, i.pc()); ei.withinInitialFrame(); ei++) {
+        RootedObject env(cx);
+        RootedScope scope(cx);
+        if (!GetFrameEnvironmentAndScope(cx, frame, i.pc(), &env, &scope))
+            return false;
+
+        for (EnvironmentIter ei(cx, env, scope, frame); ei.withinInitialFrame(); ei++) {
             if (ei.hasSyntacticEnvironment() && !ei.scope().is<GlobalScope>()) {
                 MOZ_ASSERT(ei.environment().compartment() == cx->compartment());
                 DebugEnvironments* envs = ensureCompartmentData(cx);
                 if (!envs)
                     return false;
                 if (!envs->liveEnvs.put(&ei.environment(), LiveEnvironmentVal(ei)))
                     return false;
             }
@@ -3006,17 +3028,22 @@ js::GetDebugEnvironmentForFunction(JSCon
 
 JSObject*
 js::GetDebugEnvironmentForFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc)
 {
     assertSameCompartment(cx, frame);
     if (CanUseDebugEnvironmentMaps(cx) && !DebugEnvironments::updateLiveEnvironments(cx))
         return nullptr;
 
-    EnvironmentIter ei(cx, frame, pc);
+    RootedObject env(cx);
+    RootedScope scope(cx);
+    if (!GetFrameEnvironmentAndScope(cx, frame, pc, &env, &scope))
+        return nullptr;
+
+    EnvironmentIter ei(cx, env, scope, frame);
     return GetDebugEnvironment(cx, ei);
 }
 
 JSObject*
 js::GetDebugEnvironmentForGlobalLexicalEnvironment(JSContext* cx)
 {
     EnvironmentIter ei(cx, &cx->global()->lexicalEnvironment(), &cx->global()->emptyGlobalScope());
     return GetDebugEnvironment(cx, ei);
@@ -3101,17 +3128,22 @@ js::GetModuleEnvironmentForScript(JSScri
     }
     return nullptr;
 }
 
 bool
 js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
                                              MutableHandleValue res)
 {
-    for (EnvironmentIter ei(cx, frame, pc); ei; ei++) {
+    RootedObject scopeChain(cx);
+    RootedScope scope(cx);
+    if (!GetFrameEnvironmentAndScope(cx, frame, pc, &scopeChain, &scope))
+        return false;
+
+    for (EnvironmentIter ei(cx, scopeChain, scope, frame); ei; ei++) {
         if (ei.scope().kind() == ScopeKind::Module) {
             res.setUndefined();
             return true;
         }
 
         if (!ei.scope().is<FunctionScope>() ||
             ei.scope().as<FunctionScope>().canonicalFunction()->hasLexicalThis())
         {
@@ -3175,17 +3207,16 @@ js::GetThisValueForDebuggerMaybeOptimize
                 res.setMagic(JS_OPTIMIZED_OUT);
 
             return true;
         }
 
         MOZ_CRASH("'this' binding must be found");
     }
 
-    RootedObject scopeChain(cx, frame.environmentChain());
     return GetNonSyntacticGlobalThis(cx, scopeChain, res);
 }
 
 bool
 js::CheckLexicalNameConflict(JSContext* cx, Handle<LexicalEnvironmentObject*> lexicalEnv,
                              HandleObject varObj, HandlePropertyName name)
 {
     const char* redeclKind = nullptr;
@@ -3444,16 +3475,35 @@ js::PushVarEnvironmentObject(JSContext* 
 {
     VarEnvironmentObject* env = VarEnvironmentObject::create(cx, scope, frame);
     if (!env)
         return false;
     frame.pushOnEnvironmentChain(*env);
     return true;
 }
 
+bool
+js::GetFrameEnvironmentAndScope(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
+                                MutableHandleObject env, MutableHandleScope scope)
+{
+    env.set(frame.environmentChain());
+
+    if (frame.isWasmDebugFrame()) {
+        RootedWasmInstanceObject instance(cx, frame.wasmInstance()->object());
+        uint32_t funcIndex = frame.asWasmDebugFrame()->funcIndex();
+        scope.set(WasmInstanceObject::getFunctionScope(cx, instance, funcIndex));
+        if (!scope)
+            return false;
+    } else {
+        scope.set(frame.script()->innermostScope(pc));
+    }
+    return true;
+}
+
+
 #ifdef DEBUG
 
 typedef HashSet<PropertyName*> PropertyNameSet;
 
 static bool
 RemoveReferencedNames(JSContext* cx, HandleScript script, PropertyNameSet& remainingNames)
 {
     // Remove from remainingNames --- the closure variables in some outer
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -667,16 +667,22 @@ class MOZ_RAII EnvironmentIter
     EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
     // Constructing from a frame. Places the EnvironmentIter on the innermost
     // environment at pc.
     EnvironmentIter(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
+    // Constructing from an environment, scope and frame. The frame is given
+    // to initialize to proper enclosing environment/scope.
+    EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope, AbstractFramePtr frame
+                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+
+
     bool done() const {
         return si_.done();
     }
 
     explicit operator bool() const {
         return !done();
     }
 
@@ -1115,16 +1121,20 @@ CheckEvalDeclarationConflicts(JSContext*
                               HandleObject varObj);
 
 MOZ_MUST_USE bool
 InitFunctionEnvironmentObjects(JSContext* cx, AbstractFramePtr frame);
 
 MOZ_MUST_USE bool
 PushVarEnvironmentObject(JSContext* cx, HandleScope scope, AbstractFramePtr frame);
 
+MOZ_MUST_USE bool
+GetFrameEnvironmentAndScope(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
+                            MutableHandleObject env, MutableHandleScope scope);
+
 #ifdef DEBUG
 bool
 AnalyzeEntrainedVariables(JSContext* cx, HandleScript script);
 #endif
 
 } // namespace js
 
 namespace JS {
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -152,18 +152,18 @@ struct SavedFrame::Lookup {
         asyncCause(asyncCause),
         parent(parent),
         principals(principals),
         framePtr(framePtr),
         pc(pc),
         activation(activation)
     {
         MOZ_ASSERT(source);
-        MOZ_ASSERT_IF(framePtr.isSome(), pc);
         MOZ_ASSERT_IF(framePtr.isSome(), activation);
+        MOZ_ASSERT_IF(framePtr.isSome() && !activation->isWasm(), pc);
 
 #ifdef JS_MORE_DETERMINISTIC
         column = 0;
 #endif
     }
 
     explicit Lookup(SavedFrame& savedFrame)
       : source(savedFrame.getSource()),
@@ -1319,17 +1319,17 @@ SavedStacks::insertFrames(JSContext* cx,
         }
 
         // The bit set means that the next older parent (frame, pc) pair *must*
         // be in the cache.
         if (capture.is<JS::AllFrames>())
             parentIsInCache = iter.hasCachedSavedFrame();
 
         auto principals = iter.compartment()->principals();
-        auto displayAtom = iter.isFunctionFrame() ? iter.functionDisplayAtom() : nullptr;
+        auto displayAtom = (iter.isWasm() || iter.isFunctionFrame()) ? iter.functionDisplayAtom() : nullptr;
         if (!stackChain->emplaceBack(location.source(),
                                      location.line(),
                                      location.column(),
                                      displayAtom,
                                      nullptr,
                                      nullptr,
                                      principals,
                                      LiveSavedFrameCache::getFramePtr(iter),
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -14,16 +14,18 @@
 #include "jscntxt.h"
 #include "jsscript.h"
 
 #include "jit/BaselineFrame.h"
 #include "jit/RematerializedFrame.h"
 #include "js/Debug.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/GeneratorObject.h"
+#include "wasm/WasmDebugFrame.h"
+#include "wasm/WasmInstance.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/BaselineFrame-inl.h"
 
 namespace js {
 
@@ -439,16 +441,18 @@ AbstractFramePtr::setReturnValue(const V
 
 inline JSObject*
 AbstractFramePtr::environmentChain() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->environmentChain();
     if (isBaselineFrame())
         return asBaselineFrame()->environmentChain();
+    if (isWasmDebugFrame())
+        return asWasmDebugFrame()->environmentChain();
     return asRematerializedFrame()->environmentChain();
 }
 
 template <typename SpecificEnvironment>
 inline void
 AbstractFramePtr::pushOnEnvironmentChain(SpecificEnvironment& env)
 {
     if (isInterpreterFrame()) {
@@ -575,36 +579,42 @@ AbstractFramePtr::createSingleton() cons
 
 inline bool
 AbstractFramePtr::isGlobalFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isGlobalFrame();
     if (isBaselineFrame())
         return asBaselineFrame()->isGlobalFrame();
+    if (isWasmDebugFrame())
+        return false;
     return asRematerializedFrame()->isGlobalFrame();
 }
 
 inline bool
 AbstractFramePtr::isModuleFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isModuleFrame();
     if (isBaselineFrame())
         return asBaselineFrame()->isModuleFrame();
+    if (isWasmDebugFrame())
+        return false;
     return asRematerializedFrame()->isModuleFrame();
 }
 
 inline bool
 AbstractFramePtr::isEvalFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isEvalFrame();
     if (isBaselineFrame())
         return asBaselineFrame()->isEvalFrame();
+    if (isWasmDebugFrame())
+        return false;
     MOZ_ASSERT(isRematerializedFrame());
     return false;
 }
 
 inline bool
 AbstractFramePtr::isDebuggerEvalFrame() const
 {
     if (isInterpreterFrame())
@@ -617,77 +627,108 @@ AbstractFramePtr::isDebuggerEvalFrame() 
 
 inline bool
 AbstractFramePtr::hasCachedSavedFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->hasCachedSavedFrame();
     if (isBaselineFrame())
         return asBaselineFrame()->hasCachedSavedFrame();
+    if (isWasmDebugFrame())
+        return asWasmDebugFrame()->hasCachedSavedFrame();
     return asRematerializedFrame()->hasCachedSavedFrame();
 }
 
 inline void
 AbstractFramePtr::setHasCachedSavedFrame()
 {
     if (isInterpreterFrame())
         asInterpreterFrame()->setHasCachedSavedFrame();
     else if (isBaselineFrame())
         asBaselineFrame()->setHasCachedSavedFrame();
+    else if (isWasmDebugFrame())
+        asWasmDebugFrame()->setHasCachedSavedFrame();
     else
         asRematerializedFrame()->setHasCachedSavedFrame();
 }
 
 inline bool
 AbstractFramePtr::isDebuggee() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isDebuggee();
     if (isBaselineFrame())
         return asBaselineFrame()->isDebuggee();
+    if (isWasmDebugFrame())
+        return asWasmDebugFrame()->isDebuggee();
     return asRematerializedFrame()->isDebuggee();
 }
 
 inline void
 AbstractFramePtr::setIsDebuggee()
 {
     if (isInterpreterFrame())
         asInterpreterFrame()->setIsDebuggee();
     else if (isBaselineFrame())
         asBaselineFrame()->setIsDebuggee();
+    else if (isWasmDebugFrame())
+        asWasmDebugFrame()->setIsDebuggee();
     else
         asRematerializedFrame()->setIsDebuggee();
 }
 
 inline void
 AbstractFramePtr::unsetIsDebuggee()
 {
     if (isInterpreterFrame())
         asInterpreterFrame()->unsetIsDebuggee();
     else if (isBaselineFrame())
         asBaselineFrame()->unsetIsDebuggee();
+    else if (isWasmDebugFrame())
+        asWasmDebugFrame()->unsetIsDebuggee();
     else
         asRematerializedFrame()->unsetIsDebuggee();
 }
 
 inline bool
-AbstractFramePtr::hasArgs() const {
+AbstractFramePtr::hasArgs() const
+{
     return isFunctionFrame();
 }
 
+inline bool
+AbstractFramePtr::hasScript() const
+{
+    return !isWasmDebugFrame();
+}
+
 inline JSScript*
 AbstractFramePtr::script() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->script();
     if (isBaselineFrame())
         return asBaselineFrame()->script();
     return asRematerializedFrame()->script();
 }
 
+inline wasm::Instance*
+AbstractFramePtr::wasmInstance() const
+{
+    return asWasmDebugFrame()->instance();
+}
+
+inline GlobalObject*
+AbstractFramePtr::global() const
+{
+    if (isWasmDebugFrame())
+        return &wasmInstance()->object()->global();
+    return &script()->global();
+}
+
 inline JSFunction*
 AbstractFramePtr::callee() const
 {
     if (isInterpreterFrame())
         return &asInterpreterFrame()->callee();
     if (isBaselineFrame())
         return asBaselineFrame()->callee();
     return asRematerializedFrame()->callee();
@@ -705,16 +746,18 @@ AbstractFramePtr::calleev() const
 
 inline bool
 AbstractFramePtr::isFunctionFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isFunctionFrame();
     if (isBaselineFrame())
         return asBaselineFrame()->isFunctionFrame();
+    if (isWasmDebugFrame())
+        return false;
     return asRematerializedFrame()->isFunctionFrame();
 }
 
 inline bool
 AbstractFramePtr::isNonStrictDirectEvalFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isNonStrictDirectEvalFrame();
@@ -777,44 +820,54 @@ AbstractFramePtr::initArgsObj(ArgumentsO
 
 inline bool
 AbstractFramePtr::prevUpToDate() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->prevUpToDate();
     if (isBaselineFrame())
         return asBaselineFrame()->prevUpToDate();
+    if (isWasmDebugFrame())
+        return asWasmDebugFrame()->prevUpToDate();
     return asRematerializedFrame()->prevUpToDate();
 }
 
 inline void
 AbstractFramePtr::setPrevUpToDate() const
 {
     if (isInterpreterFrame()) {
         asInterpreterFrame()->setPrevUpToDate();
         return;
     }
     if (isBaselineFrame()) {
         asBaselineFrame()->setPrevUpToDate();
         return;
     }
+    if (isWasmDebugFrame()) {
+        asWasmDebugFrame()->setPrevUpToDate();
+        return;
+    }
     asRematerializedFrame()->setPrevUpToDate();
 }
 
 inline void
 AbstractFramePtr::unsetPrevUpToDate() const
 {
     if (isInterpreterFrame()) {
         asInterpreterFrame()->unsetPrevUpToDate();
         return;
     }
     if (isBaselineFrame()) {
         asBaselineFrame()->unsetPrevUpToDate();
         return;
     }
+    if (isWasmDebugFrame()) {
+        asWasmDebugFrame()->unsetPrevUpToDate();
+        return;
+    }
     asRematerializedFrame()->unsetPrevUpToDate();
 }
 
 inline Value&
 AbstractFramePtr::thisArgument() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->thisArgument();
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -12,16 +12,17 @@
 
 #include "gc/Marking.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitCompartment.h"
 #include "js/GCAPI.h"
 #include "vm/Debugger.h"
 #include "vm/Opcodes.h"
+#include "wasm/WasmDebugFrame.h"
 
 #include "jit/JitFrameIterator-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/Probes-inl.h"
 
 using namespace js;
 
@@ -546,17 +547,17 @@ FrameIter::settleOnActivation()
         if (activation->isWasm()) {
             data_.wasmFrames_ = wasm::FrameIterator(*data_.activations_->asWasm());
 
             if (data_.wasmFrames_.done()) {
                 ++data_.activations_;
                 continue;
             }
 
-            data_.pc_ = (jsbytecode*)data_.wasmFrames_.pc();
+            data_.pc_ = nullptr;
             data_.state_ = WASM;
             return;
         }
 
         MOZ_ASSERT(activation->isInterpreter());
 
         InterpreterActivation* interpAct = activation->asInterpreter();
         data_.interpFrames_ = InterpreterFrameIterator(interpAct);
@@ -679,17 +680,17 @@ FrameIter::popJitFrame()
 }
 
 void
 FrameIter::popWasmFrame()
 {
     MOZ_ASSERT(data_.state_ == WASM);
 
     ++data_.wasmFrames_;
-    data_.pc_ = (jsbytecode*)data_.wasmFrames_.pc();
+    data_.pc_ = nullptr;
     if (data_.wasmFrames_.done())
         popActivation();
 }
 
 FrameIter&
 FrameIter::operator++()
 {
     switch (data_.state_) {
@@ -727,17 +728,16 @@ FrameIter::operator++()
 
 FrameIter::Data*
 FrameIter::copyData() const
 {
     Data* data = data_.cx_->new_<Data>(data_);
     if (!data)
         return nullptr;
 
-    MOZ_ASSERT(data_.state_ != WASM);
     if (data && data_.jitFrames_.isIonScripted())
         data->ionInlineFrameNo_ = ionInlineFrames_.frameNo();
     return data;
 }
 
 AbstractFramePtr
 FrameIter::copyDataAsAbstractFramePtr() const
 {
@@ -753,17 +753,17 @@ FrameIter::rawFramePtr() const
     switch (data_.state_) {
       case DONE:
         return nullptr;
       case JIT:
         return data_.jitFrames_.fp();
       case INTERP:
         return interpFrame();
       case WASM:
-        return data_.wasmFrames_.fp();
+        return nullptr;
     }
     MOZ_CRASH("Unexpected state");
 }
 
 JSCompartment*
 FrameIter::compartment() const
 {
     switch (data_.state_) {
@@ -805,33 +805,33 @@ FrameIter::isFunctionFrame() const
         break;
       case INTERP:
         return interpFrame()->isFunctionFrame();
       case JIT:
         if (data_.jitFrames_.isBaselineJS())
             return data_.jitFrames_.baselineFrame()->isFunctionFrame();
         return script()->functionNonDelazifying();
       case WASM:
-        return true;
+        return false;
     }
     MOZ_CRASH("Unexpected state");
 }
 
 JSAtom*
 FrameIter::functionDisplayAtom() const
 {
-    MOZ_ASSERT(isFunctionFrame());
-
     switch (data_.state_) {
       case DONE:
         break;
       case INTERP:
       case JIT:
+        MOZ_ASSERT(isFunctionFrame());
         return calleeTemplate()->displayAtom();
       case WASM:
+        MOZ_ASSERT(isWasm());
         return data_.wasmFrames_.functionDisplayAtom();
     }
 
     MOZ_CRASH("Unexpected state");
 }
 
 ScriptSource*
 FrameIter::scriptSource() const
@@ -940,60 +940,64 @@ FrameIter::ensureHasRematerializedFrame(
     return !!activation()->asJit()->getRematerializedFrame(cx, data_.jitFrames_);
 }
 
 bool
 FrameIter::hasUsableAbstractFramePtr() const
 {
     switch (data_.state_) {
       case DONE:
-      case WASM:
         return false;
       case JIT:
         if (data_.jitFrames_.isBaselineJS())
             return true;
 
         MOZ_ASSERT(data_.jitFrames_.isIonScripted());
         return !!activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
                                                                   ionInlineFrames_.frameNo());
         break;
       case INTERP:
         return true;
+      case WASM:
+        return data_.wasmFrames_.debugEnabled();
     }
     MOZ_CRASH("Unexpected state");
 }
 
 AbstractFramePtr
 FrameIter::abstractFramePtr() const
 {
     MOZ_ASSERT(hasUsableAbstractFramePtr());
     switch (data_.state_) {
       case DONE:
-      case WASM:
         break;
       case JIT: {
         if (data_.jitFrames_.isBaselineJS())
             return data_.jitFrames_.baselineFrame();
 
         MOZ_ASSERT(data_.jitFrames_.isIonScripted());
         return activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
                                                                 ionInlineFrames_.frameNo());
         break;
       }
       case INTERP:
         MOZ_ASSERT(interpFrame());
         return AbstractFramePtr(interpFrame());
+      case WASM:
+        MOZ_ASSERT(data_.wasmFrames_.debugEnabled());
+        return data_.wasmFrames_.debugFrame();
     }
     MOZ_CRASH("Unexpected state");
 }
 
 void
 FrameIter::updatePcQuadratic()
 {
     switch (data_.state_) {
+      case WASM:
       case DONE:
         break;
       case INTERP: {
         InterpreterFrame* frame = interpFrame();
         InterpreterActivation* activation = data_.activations_->asInterpreter();
 
         // Look for the current frame.
         data_.interpFrames_ = InterpreterFrameIterator(activation);
@@ -1022,20 +1026,16 @@ FrameIter::updatePcQuadratic()
                 ++data_.jitFrames_;
 
             // Update the pc.
             MOZ_ASSERT(data_.jitFrames_.baselineFrame() == frame);
             data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
             return;
         }
         break;
-      case WASM:
-        // Update the pc.
-        data_.pc_ = (jsbytecode*)data_.wasmFrames_.pc();
-        break;
     }
     MOZ_CRASH("Unexpected state");
 }
 
 JSFunction*
 FrameIter::calleeTemplate() const
 {
     switch (data_.state_) {
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -48,16 +48,17 @@ class EnvironmentIter;
 class EnvironmentCoordinate;
 
 class SavedFrame;
 
 namespace jit {
 class CommonFrameLayout;
 }
 namespace wasm {
+class DebugFrame;
 class Instance;
 }
 
 // VM stack layout
 //
 // A JSRuntime's stack consists of a linked list of activations. Every activation
 // contains a number of scripted frames that are either running in the interpreter
 // (InterpreterActivation) or JIT code (JitActivation). The frames inside a single
@@ -121,17 +122,18 @@ class AbstractFramePtr
 
     uintptr_t ptr_;
 
     enum {
         Tag_ScriptFrameIterData = 0x0,
         Tag_InterpreterFrame = 0x1,
         Tag_BaselineFrame = 0x2,
         Tag_RematerializedFrame = 0x3,
-        TagMask = 0x3
+        Tag_WasmDebugFrame = 0x4,
+        TagMask = 0x7
     };
 
   public:
     AbstractFramePtr()
       : ptr_(0)
     {}
 
     MOZ_IMPLICIT AbstractFramePtr(InterpreterFrame* fp)
@@ -147,16 +149,22 @@ class AbstractFramePtr
     }
 
     MOZ_IMPLICIT AbstractFramePtr(jit::RematerializedFrame* fp)
       : ptr_(fp ? uintptr_t(fp) | Tag_RematerializedFrame : 0)
     {
         MOZ_ASSERT_IF(fp, asRematerializedFrame() == fp);
     }
 
+    MOZ_IMPLICIT AbstractFramePtr(wasm::DebugFrame* fp)
+      : ptr_(fp ? uintptr_t(fp) | Tag_WasmDebugFrame : 0)
+    {
+        MOZ_ASSERT_IF(fp, asWasmDebugFrame() == fp);
+    }
+
     static AbstractFramePtr FromRaw(void* raw) {
         AbstractFramePtr frame;
         frame.ptr_ = uintptr_t(raw);
         return frame;
     }
 
     bool isScriptFrameIterData() const {
         return !!ptr_ && (ptr_ & TagMask) == Tag_ScriptFrameIterData;
@@ -183,16 +191,25 @@ class AbstractFramePtr
         return (ptr_ & TagMask) == Tag_RematerializedFrame;
     }
     jit::RematerializedFrame* asRematerializedFrame() const {
         MOZ_ASSERT(isRematerializedFrame());
         jit::RematerializedFrame* res = (jit::RematerializedFrame*)(ptr_ & ~TagMask);
         MOZ_ASSERT(res);
         return res;
     }
+    bool isWasmDebugFrame() const {
+        return (ptr_ & TagMask) == Tag_WasmDebugFrame;
+    }
+    wasm::DebugFrame* asWasmDebugFrame() const {
+        MOZ_ASSERT(isWasmDebugFrame());
+        wasm::DebugFrame* res = (wasm::DebugFrame*)(ptr_ & ~TagMask);
+        MOZ_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_; }
 
     explicit operator bool() const { return !!ptr_; }
 
@@ -210,17 +227,20 @@ class AbstractFramePtr
     inline bool hasInitialEnvironment() const;
     inline bool isGlobalFrame() const;
     inline bool isModuleFrame() const;
     inline bool isEvalFrame() const;
     inline bool isDebuggerEvalFrame() const;
     inline bool hasCachedSavedFrame() const;
     inline void setHasCachedSavedFrame();
 
+    inline bool hasScript() const;
     inline JSScript* script() const;
+    inline wasm::Instance* wasmInstance() const;
+    inline GlobalObject* global() const;
     inline JSFunction* callee() const;
     inline Value calleev() const;
     inline Value& thisArgument() const;
 
     inline Value newTarget() const;
 
     inline bool debuggerNeedsCheckPrimitiveReturn() const;
 
@@ -254,16 +274,17 @@ class AbstractFramePtr
 
     inline HandleValue returnValue() const;
     inline void setReturnValue(const Value& rval) const;
 
     friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, void*);
     friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, InterpreterFrame*);
     friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, jit::BaselineFrame*);
     friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, jit::RematerializedFrame*);
+    friend void GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, wasm::DebugFrame* ptr);
 };
 
 class NullFramePtr : public AbstractFramePtr
 {
   public:
     NullFramePtr()
       : AbstractFramePtr()
     { }
@@ -1783,16 +1804,21 @@ class FrameIter
     const char16_t* displayURL() const;
     unsigned computeLine(uint32_t* column = nullptr) const;
     JSAtom* functionDisplayAtom() const;
     bool mutedErrors() const;
 
     bool hasScript() const { return !isWasm(); }
 
     // -----------------------------------------------------------
+    //  The following functions can only be called when isWasm()
+    // -----------------------------------------------------------
+    inline wasm::Instance* wasmInstance() const;
+
+    // -----------------------------------------------------------
     // The following functions can only be called when hasScript()
     // -----------------------------------------------------------
 
     inline JSScript* script() const;
 
     bool        isConstructing() const;
     jsbytecode* pc() const { MOZ_ASSERT(!done()); return data_.pc_; }
     void        updatePcQuadratic();
@@ -1843,17 +1869,17 @@ class FrameIter
     bool ensureHasRematerializedFrame(JSContext* cx);
 
     // True when isInterp() or isBaseline(). True when isIon() if it
     // has a rematerialized frame. False otherwise false otherwise.
     bool hasUsableAbstractFramePtr() const;
 
     // -----------------------------------------------------------
     // The following functions can only be called when isInterp(),
-    // isBaseline(), or isIon(). Further, abstractFramePtr() can
+    // isBaseline(), isWasm() or isIon(). Further, abstractFramePtr() can
     // only be called when hasUsableAbstractFramePtr().
     // -----------------------------------------------------------
 
     AbstractFramePtr abstractFramePtr() const;
     AbstractFramePtr copyDataAsAbstractFramePtr() const;
     Data* copyData() const;
 
     // This can only be called when isInterp():
@@ -2025,16 +2051,24 @@ FrameIter::script() const
     if (data_.state_ == INTERP)
         return interpFrame()->script();
     MOZ_ASSERT(data_.state_ == JIT);
     if (data_.jitFrames_.isIonJS())
         return ionInlineFrames_.script();
     return data_.jitFrames_.script();
 }
 
+inline wasm::Instance*
+FrameIter::wasmInstance() const
+{
+    MOZ_ASSERT(!done());
+    MOZ_ASSERT(data_.state_ == WASM);
+    return data_.wasmFrames_.instance();
+}
+
 inline bool
 FrameIter::isIon() const
 {
     return isJit() && data_.jitFrames_.isIonJS();
 }
 
 inline bool
 FrameIter::isBaseline() const
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -99,16 +99,17 @@
 # include "jit/arm/Assembler-arm.h"
 #endif
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
 # include "jit/x86-shared/Architecture-x86-shared.h"
 # include "jit/x86-shared/Assembler-x86-shared.h"
 #endif
 
 #include "wasm/WasmBinaryIterator.h"
+#include "wasm/WasmDebugFrame.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmValidate.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using mozilla::DebugOnly;
 using mozilla::FloatingPoint;
@@ -2132,16 +2133,24 @@ class BaseCompiler
               default:
                 MOZ_CRASH("Function argument type");
             }
         }
 
         // The TLS pointer is always passed as a hidden argument in WasmTlsReg.
         // Save it into its assigned local slot.
         storeToFramePtr(WasmTlsReg, localInfo_[tlsSlot_].offs());
+        if (debugEnabled_) {
+            // Initialize funcIndex and flag fields of DebugFrame.
+            size_t debugFrame = masm.framePushed() - DebugFrame::offsetOfFrame();
+            masm.store32(Imm32(func_.index()),
+                         Address(masm.getStackPointer(), debugFrame + DebugFrame::offsetOfFuncIndex()));
+            masm.storePtr(ImmWord(0),
+                          Address(masm.getStackPointer(), debugFrame + DebugFrame::offsetOfFlagsWord()));
+        }
 
         // Initialize the stack locals to zero.
         //
         // The following are all Bug 1316820:
         //
         // TODO / OPTIMIZE: on x64, at least, scratch will be a 64-bit
         // register and we can move 64 bits at a time.
         //
@@ -7770,16 +7779,27 @@ BaseCompiler::init()
     // entries for special locals. Currently the only special local is the TLS
     // pointer.
     tlsSlot_ = locals_.length();
     if (!localInfo_.resize(locals_.length() + 1))
         return false;
 
     localSize_ = 0;
 
+    // Reserve a stack slot for the TLS pointer outside the varLow..varHigh
+    // range so it isn't zero-filled like the normal locals.
+    localInfo_[tlsSlot_].init(MIRType::Pointer, pushLocal(sizeof(void*)));
+    if (debugEnabled_) {
+        // If debug information is generated, constructing DebugFrame record:
+        // reserving some data before TLS pointer. The TLS pointer allocated
+        // above and regular wasm::Frame data starts after locals.
+        localSize_ += DebugFrame::offsetOfTlsData();
+        MOZ_ASSERT(DebugFrame::offsetOfFrame() == localSize_);
+    }
+
     for (ABIArgIter<const ValTypeVector> i(args); !i.done(); i++) {
         Local& l = localInfo_[i.index()];
         switch (i.mirType()) {
           case MIRType::Int32:
             if (i->argInRegister())
                 l.init(MIRType::Int32, pushLocal(4));
             else
                 l.init(MIRType::Int32, -(i->offsetFromArgBase() + sizeof(Frame)));
@@ -7802,20 +7822,16 @@ BaseCompiler::init()
             else
                 l.init(MIRType::Float32, -(i->offsetFromArgBase() + sizeof(Frame)));
             break;
           default:
             MOZ_CRASH("Argument type");
         }
     }
 
-    // Reserve a stack slot for the TLS pointer outside the varLow..varHigh
-    // range so it isn't zero-filled like the normal locals.
-    localInfo_[tlsSlot_].init(MIRType::Pointer, pushLocal(sizeof(void*)));
-
     varLow_ = localSize_;
 
     for (size_t i = args.length(); i < locals_.length(); i++) {
         Local& l = localInfo_[i];
         switch (locals_[i]) {
           case ValType::I32:
             l.init(MIRType::Int32, pushLocal(4));
             break;
new file mode 100644
--- /dev/null
+++ b/js/src/wasm/WasmDebugFrame.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wasm/WasmDebugFrame.h"
+
+#include "vm/EnvironmentObject.h"
+#include "wasm/WasmInstance.h"
+
+#include "jsobjinlines.h"
+
+using namespace js;
+using namespace js::wasm;
+
+Instance*
+DebugFrame::instance() const
+{
+    return tlsData_->instance;
+}
+
+GlobalObject*
+DebugFrame::global() const
+{
+    return &instance()->object()->global();
+}
+
+JSObject*
+DebugFrame::environmentChain() const
+{
+    return &global()->lexicalEnvironment();
+}
+
+void
+DebugFrame::observeFrame(JSContext* cx)
+{
+   if (observing_)
+       return;
+
+   // TODO make sure wasm::Code onLeaveFrame traps are on
+   observing_ = true;
+}
+
+void
+DebugFrame::leaveFrame(JSContext* cx)
+{
+   if (!observing_)
+       return;
+
+   // TODO make sure wasm::Code onLeaveFrame traps are off
+   observing_ = false;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/wasm/WasmDebugFrame.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasmdebugframe_js_h
+#define wasmdebugframe_js_h
+
+#include "gc/Barrier.h"
+#include "js/RootingAPI.h"
+#include "js/TracingAPI.h"
+#include "wasm/WasmTypes.h"
+
+namespace js {
+
+class WasmFunctionCallObject;
+
+namespace wasm {
+
+class DebugFrame
+{
+    union
+    {
+        int32_t resultI32_;
+        int64_t resultI64_;
+        float   resultF32_;
+        double  resultF64_;
+    };
+
+    // The fields below are initialized by the baseline compiler.
+    uint32_t    funcIndex_;
+    uint32_t    reserved0_;
+
+    union
+    {
+        struct
+        {
+            bool    observing_ : 1;
+            bool    isDebuggee_ : 1;
+            bool    prevUpToDate_ : 1;
+            bool    hasCachedSavedFrame_ : 1;
+        };
+        void*   reserved1_;
+    };
+
+    TlsData*    tlsData_;
+    Frame       frame_;
+
+    explicit DebugFrame() {}
+
+  public:
+    inline uint32_t funcIndex() const { return funcIndex_; }
+    inline TlsData* tlsData() const { return tlsData_; }
+    inline Frame& frame() { return frame_; }
+
+    Instance* instance() const;
+    GlobalObject* global() const;
+
+    JSObject* environmentChain() const;
+
+    void observeFrame(JSContext* cx);
+    void leaveFrame(JSContext* cx);
+
+    void trace(JSTracer* trc);
+
+    // These are opaque boolean flags used by the debugger and
+    // saved-frame-chains code.
+    inline bool isDebuggee() const { return isDebuggee_; }
+    inline void setIsDebuggee() { isDebuggee_ = true; }
+    inline void unsetIsDebuggee() { isDebuggee_ = false; }
+
+    inline bool prevUpToDate() const { return prevUpToDate_; }
+    inline void setPrevUpToDate() { prevUpToDate_ = true; }
+    inline void unsetPrevUpToDate() { prevUpToDate_ = false; }
+
+    inline bool hasCachedSavedFrame() const { return hasCachedSavedFrame_; }
+    inline void setHasCachedSavedFrame() { hasCachedSavedFrame_ = true; }
+
+    inline void* resultsPtr() { return &resultI32_; }
+
+    static constexpr size_t offsetOfResults() { return offsetof(DebugFrame, resultI32_); }
+    static constexpr size_t offsetOfFlagsWord() { return offsetof(DebugFrame, reserved1_); }
+    static constexpr size_t offsetOfFuncIndex() { return offsetof(DebugFrame, funcIndex_); }
+    static constexpr size_t offsetOfTlsData() { return offsetof(DebugFrame, tlsData_); }
+    static constexpr size_t offsetOfFrame() { return offsetof(DebugFrame, frame_); }
+};
+
+static_assert(DebugFrame::offsetOfResults() == 0, "results shall be at offset 0");
+static_assert(DebugFrame::offsetOfTlsData() + sizeof(TlsData*) == DebugFrame::offsetOfFrame(),
+              "TLS pointer must be a field just before the wasm frame");
+static_assert(sizeof(DebugFrame) % 8 == 0 && DebugFrame::offsetOfFrame() % 8 == 0,
+              "DebugFrame and its portion is 8-bytes aligned for AbstractFramePtr");
+
+} // namespace wasm
+} // namespace js
+
+#endif // wasmdebugframe_js_h
--- a/js/src/wasm/WasmFrameIterator.cpp
+++ b/js/src/wasm/WasmFrameIterator.cpp
@@ -13,16 +13,17 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmFrameIterator.h"
 
+#include "wasm/WasmDebugFrame.h"
 #include "wasm/WasmInstance.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
@@ -39,16 +40,23 @@ ReturnAddressFromFP(void* fp)
 }
 
 static uint8_t*
 CallerFPFromFP(void* fp)
 {
     return reinterpret_cast<Frame*>(fp)->callerFP;
 }
 
+static TlsData*
+TlsDataFromFP(void *fp)
+{
+    void* debugFrame = (uint8_t*)fp - DebugFrame::offsetOfFrame();
+    return reinterpret_cast<DebugFrame*>(debugFrame)->tlsData();
+}
+
 FrameIterator::FrameIterator()
   : activation_(nullptr),
     code_(nullptr),
     callsite_(nullptr),
     codeRange_(nullptr),
     fp_(nullptr),
     pc_(nullptr),
     missingFrameMessage_(false)
@@ -203,16 +211,40 @@ FrameIterator::functionDisplayAtom() con
 unsigned
 FrameIterator::lineOrBytecode() const
 {
     MOZ_ASSERT(!done());
     return callsite_ ? callsite_->lineOrBytecode()
                      : (codeRange_ ? codeRange_->funcLineOrBytecode() : 0);
 }
 
+Instance*
+FrameIterator::instance() const
+{
+    MOZ_ASSERT(!done() && debugEnabled());
+    return TlsDataFromFP(fp_ + callsite_->stackDepth())->instance;
+}
+
+bool
+FrameIterator::debugEnabled() const
+{
+    MOZ_ASSERT(!done() && code_);
+    MOZ_ASSERT_IF(!missingFrameMessage_, codeRange_->kind() == CodeRange::Function);
+    return code_->metadata().debugEnabled;
+}
+
+DebugFrame*
+FrameIterator::debugFrame() const
+{
+    MOZ_ASSERT(!done() && debugEnabled());
+    // The fp() points to wasm::Frame.
+    void* buf = static_cast<uint8_t*>(fp_ + callsite_->stackDepth()) - DebugFrame::offsetOfFrame();
+    return static_cast<DebugFrame*>(buf);
+}
+
 /*****************************************************************************/
 // Prologue/epilogue code generation
 
 // These constants reflect statically-determined offsets in the profiling
 // prologue/epilogue. The offsets are dynamically asserted during code
 // generation.
 #if defined(JS_CODEGEN_X64)
 # if defined(DEBUG)
--- a/js/src/wasm/WasmFrameIterator.h
+++ b/js/src/wasm/WasmFrameIterator.h
@@ -28,16 +28,18 @@ namespace js {
 class WasmActivation;
 namespace jit { class MacroAssembler; }
 
 namespace wasm {
 
 class CallSite;
 class Code;
 class CodeRange;
+class DebugFrame;
+class Instance;
 class SigIdDesc;
 struct CallThunk;
 struct FuncOffsets;
 struct ProfilingOffsets;
 struct TrapOffset;
 
 // Iterates over the frames of a single WasmActivation, called synchronously
 // from C++ in the thread of the asm.js.
@@ -64,18 +66,20 @@ class FrameIterator
     explicit FrameIterator(const WasmActivation& activation);
     void operator++();
     bool done() const;
     const char* filename() const;
     const char16_t* displayURL() const;
     bool mutedErrors() const;
     JSAtom* functionDisplayAtom() const;
     unsigned lineOrBytecode() const;
-    inline void* fp() const { return fp_; }
-    inline uint8_t* pc() const { return pc_; }
+    const CodeRange* codeRange() const { return codeRange_; }
+    Instance* instance() const;
+    bool debugEnabled() const;
+    DebugFrame* debugFrame() const;
 };
 
 // An ExitReason describes the possible reasons for leaving compiled wasm code
 // or the state of not having left compiled wasm code (ExitReason::None).
 enum class ExitReason : uint32_t
 {
     None,          // default state, the pc is in wasm code
     ImportJit,     // fast-path call directly into JIT code
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1485,12 +1485,34 @@ struct MemoryPatch
 
     void offsetBy(uint32_t delta) {
         offset += delta;
     }
 };
 
 WASM_DECLARE_POD_VECTOR(MemoryPatch, MemoryPatchVector)
 
+// As an invariant across architectures, within wasm code:
+//   $sp % WasmStackAlignment = (sizeof(wasm::Frame) + masm.framePushed) % WasmStackAlignment
+// Thus, wasm::Frame represents the bytes pushed after the call (which occurred
+// with a WasmStackAlignment-aligned StackPointer) that are not included in
+// masm.framePushed.
+
+struct Frame
+{
+    // The caller's saved frame pointer. In non-profiling mode, internal
+    // wasm-to-wasm calls don't update fp and thus don't save the caller's
+    // frame pointer; the space is reserved, however, so that profiling mode can
+    // reuse the same function body without recompiling.
+    uint8_t* callerFP;
+
+    // The return address pushed by the call (in the case of ARM/MIPS the return
+    // address is pushed by the first instruction of the prologue).
+    void* returnAddress;
+};
+
+static_assert(sizeof(Frame) == 2 * sizeof(void*), "?!");
+static const uint32_t FrameBytesAfterReturnAddress = sizeof(void*);
+
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_types_h