Bug 1286948 - [WIP] Inspecting wasm frame vars. draft
authorYury Delendik <ydelendik@mozilla.com>
Tue, 18 Oct 2016 16:25:31 -0500
changeset 434997 59badd983dac506af5a684666a420a2feef3542c
parent 434996 c6243fca4f8f98e36d74104cedf4e25fde392b47
child 536189 c38f4817c2ffcadc16692d4bb2a3bd41f6ca3e7c
push id34906
push userydelendik@mozilla.com
push dateMon, 07 Nov 2016 22:16:36 +0000
bugs1286948
milestone52.0a1
Bug 1286948 - [WIP] Inspecting wasm frame vars. MozReview-Commit-ID: 9Pbt13obfa2
js/src/vm/EnvironmentObject.cpp
js/src/vm/EnvironmentObject.h
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmCode.h
js/src/wasm/WasmDebugFrame.cpp
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmGenerator.h
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmInstance.h
js/src/wasm/WasmIonCompile.h
js/src/wasm/WasmTypes.h
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -16,16 +16,17 @@
 #include "builtin/ModuleObject.h"
 #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/StringBuffer.h"
 #include "vm/Xdr.h"
 #include "wasm/WasmInstance.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Stack-inl.h"
@@ -628,25 +629,45 @@ ModuleEnvironmentObject::enumerate(JSCon
         properties.infallibleAppend(r.front().propid());
 
     MOZ_ASSERT(properties.length() == count);
     return true;
 }
 
 /*****************************************************************************/
 
+const ObjectOps WasmFunctionCallObject::objectOps_ = {
+    WasmFunctionCallObject::lookupProperty,
+    nullptr,                                             /* defineProperty */
+    WasmFunctionCallObject::hasProperty,
+    WasmFunctionCallObject::getProperty,
+    WasmFunctionCallObject::setProperty,
+    WasmFunctionCallObject::getOwnPropertyDescriptor,
+    WasmFunctionCallObject::deleteProperty,
+    nullptr, nullptr,                                    /* watch/unwatch */
+    nullptr,                                             /* getElements */
+    WasmFunctionCallObject::enumerate,
+    nullptr
+};
+
 const Class WasmFunctionCallObject::class_ = {
     "WasmCall",
-    JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(WasmFunctionCallObject::RESERVED_SLOTS)
+    JSCLASS_HAS_RESERVED_SLOTS(WasmFunctionCallObject::RESERVED_SLOTS) |
+    JSCLASS_IS_ANONYMOUS,
+    JS_NULL_CLASS_OPS,
+    JS_NULL_CLASS_SPEC,
+    JS_NULL_CLASS_EXT,
+    &WasmFunctionCallObject::objectOps_
 };
 
 /* static */  WasmFunctionCallObject*
 WasmFunctionCallObject::create(ExclusiveContext* cx,
                                HandleWasmInstanceObject instance,
-                               uint32_t funcIndex)
+                               uint32_t funcIndex,
+                               void* fp)
 {
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
     if (!group)
         return nullptr;
 
     MOZ_ASSERT(cx->isJSContext());
     Rooted<WasmFunctionScope*> scope(cx, &instance->funcScope(cx->asJSContext(), funcIndex)->as<WasmFunctionScope>());
     if (!scope)
@@ -661,16 +682,17 @@ WasmFunctionCallObject::create(Exclusive
 
     JSObject* obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, group);
     if (!obj)
         return nullptr;
 
     Rooted<WasmFunctionCallObject*> callobj(cx, &obj->as<WasmFunctionCallObject>());
     callobj->initFixedSlot(INSTANCE_SLOT, ObjectOrNullValue(instance));
     callobj->initFixedSlot(FUNC_INDEX_SLOT, NumberValue(funcIndex));
+    callobj->initFixedSlot(FRAME_POINTER_SLOT, PrivateValue(fp));
 
     callobj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
 
     return callobj;
 }
 
 WasmInstanceObject&
 WasmFunctionCallObject::wasmInstance() const
@@ -679,16 +701,133 @@ WasmFunctionCallObject::wasmInstance() c
 }
 
 uint32_t
 WasmFunctionCallObject::funcIndex() const
 {
     return getReservedSlot(FUNC_INDEX_SLOT).toInt32();
 }
 
+void*
+WasmFunctionCallObject::fp() const
+{
+    return getReservedSlot(FRAME_POINTER_SLOT).toPrivate();
+}
+
+/* static */ bool
+WasmFunctionCallObject::lookupProperty(JSContext* cx, HandleObject obj, HandleId id,
+                           MutableHandleObject objp, MutableHandleShape propp)
+{
+    // TODO implement lookupProperty
+    MarkNonNativePropertyFound<CanGC>(propp);
+    objp.set(obj);
+    return true;
+}
+
+/* static */ bool
+WasmFunctionCallObject::hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
+{
+    // TODO implement hasProperty
+    *foundp = true;
+    return true;
+}
+
+/* static */ bool
+WasmFunctionCallObject::getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
+                        MutableHandleValue vp)
+{
+    uint32_t index = 0;
+    jit::MIRType type;
+    int32_t offset;
+    wasm::Instance& instance = obj->as<WasmFunctionCallObject>().wasmInstance().instance();
+    if (instance.getVarTypeAndOffset(cx, obj->as<WasmFunctionCallObject>().funcIndex(), index, &type, &offset)) {
+        uint8_t* fp = static_cast<uint8_t*>(obj->as<WasmFunctionCallObject>().fp());
+        fp -= offset;
+        RootedValue value(cx);
+        switch (type) {
+          case jit::MIRType::Int32:
+            value.setNumber(*static_cast<uint32_t*>((void*)fp));
+            break;
+          case jit::MIRType::Int64:
+            value.setNumber((double)*static_cast<int64_t*>((void*)fp));
+            break;
+          case jit::MIRType::Float32:
+            value.setNumber(*static_cast<float*>((void*)fp));
+            break;
+          case jit::MIRType::Double:
+            value.setNumber(*static_cast<double*>((void*)fp));
+            break;
+          default:
+            MOZ_CRASH("Unexpected wasm type");
+            break;
+        }
+        vp.set(value);
+        return true;
+    }
+
+    // TODO implement getProperty
+    RootedValue value(cx, UndefinedValue());
+    vp.set(value);
+    return true;
+}
+
+/* static */ bool
+WasmFunctionCallObject::setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
+                        HandleValue receiver, JS::ObjectOpResult& result)
+{
+   // TODO implement setProperty
+    return true;
+}
+
+/* static */ bool
+WasmFunctionCallObject::getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
+                                     MutableHandle<PropertyDescriptor> desc)
+{
+    // TODO implement getOwnPropertyDescriptor
+    desc.setAttributes(JSPROP_ENUMERATE | JSPROP_PERMANENT);
+    desc.object().set(obj);
+    RootedValue value(cx, UndefinedValue());
+    desc.setValue(value);
+    desc.assertComplete();
+    return true;
+}
+
+/* static */ bool
+WasmFunctionCallObject::deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
+                           ObjectOpResult& result)
+{
+    return result.failCantDelete();
+}
+
+/* static */ bool
+WasmFunctionCallObject::enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+                      bool enumerableOnly)
+{
+    MOZ_ASSERT(properties.length() == 0);
+    wasm::Instance& instance = obj->as<WasmFunctionCallObject>().wasmInstance().instance();
+    size_t count = instance.getVarsCount(cx, obj->as<WasmFunctionCallObject>().funcIndex());
+    if (count > 9) count = 9;
+    if (!properties.reserve(count)) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    // TODO properties.infallibleAppend for each var
+    for (size_t i = 0; i < count; i++) {
+        StringBuffer tmp(cx);
+        if (!tmp.append("var") || !tmp.append((char)('0' + i)))
+            return false;
+        Rooted<JSString*> str(cx, tmp.finishString());
+        jsid id = INTERNED_STRING_TO_JSID(cx, JS_AtomizeAndPinJSString(cx, str));
+        properties.infallibleAppend(id);
+    }
+
+    MOZ_ASSERT(properties.length() == count);
+    return true;
+}
 
 /*****************************************************************************/
 
 WithEnvironmentObject*
 WithEnvironmentObject::create(JSContext* cx, HandleObject object, HandleObject enclosing,
                               Handle<WithScope*> scope)
 {
     Rooted<WithEnvironmentObject*> obj(cx);
@@ -1476,16 +1615,20 @@ class DebugEnvironmentProxyHandler : pub
         *accessResult = ACCESS_GENERIC;
         LiveEnvironmentVal* maybeLiveEnv = DebugEnvironments::hasLiveEnvironment(*env);
 
         if (env->is<ModuleEnvironmentObject>()) {
             /* Everything is aliased and stored in the environment object. */
             return true;
         }
 
+        if (env->is<WasmFunctionCallObject>()) {
+             return true; // TODO
+        }
+
         /* Handle unaliased formals, vars, lets, and consts at function scope. */
         if (env->is<CallObject>()) {
             CallObject& callobj = env->as<CallObject>();
             RootedScript script(cx, callobj.callee().getOrCreateScript(cx));
             if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
                 return false;
 
             BindingIter bi(script);
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -425,31 +425,56 @@ class ModuleEnvironmentObject : public E
 typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
 typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
 typedef MutableHandle<ModuleEnvironmentObject*> MutableHandleModuleEnvironmentObject;
 
 class WasmFunctionCallObject : public EnvironmentObject
 {
     static const uint32_t INSTANCE_SLOT = 1;
     static const uint32_t FUNC_INDEX_SLOT = 2;
+    static const uint32_t FRAME_POINTER_SLOT = 3;
+
+    static const ObjectOps objectOps_;
 
   public:
     static const Class class_;
 
-    static const uint32_t RESERVED_SLOTS = 3;
+    static const uint32_t RESERVED_SLOTS = 4;
 
     static WasmFunctionCallObject* create(ExclusiveContext* cx,
                                           HandleWasmInstanceObject instance,
-                                          uint32_t funcIndex);
+                                          uint32_t funcIndex,
+                                          void* fp);
 
     WasmInstanceObject& wasmInstance() const;
 
     uint32_t funcIndex() const;
+
+    void* fp() const;
+
+  private:
+    static bool lookupProperty(JSContext* cx, HandleObject obj, HandleId id,
+                               MutableHandleObject objp, MutableHandleShape propp);
+    static bool hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
+    static bool getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
+                            MutableHandleValue vp);
+    static bool setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
+                            HandleValue receiver, JS::ObjectOpResult& result);
+    static bool getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
+                                         MutableHandle<PropertyDescriptor> desc);
+    static bool deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
+                               ObjectOpResult& result);
+    static bool enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+                          bool enumerableOnly);
 };
 
+typedef Rooted<WasmFunctionCallObject*> RootedWasmFunctionCallObject;
+typedef Handle<WasmFunctionCallObject*> HandleWasmFunctionCallObject;
+typedef MutableHandle<WasmFunctionCallObject*> MutableHandleWasmFunctionCallObject;
+
 class LexicalEnvironmentObject : public EnvironmentObject
 {
     // Global and non-syntactic lexical environments need to store a 'this'
     // value and all other lexical environments have a fixed shape and store a
     // backpointer to the LexicalScope.
     //
     // Since the two sets are disjoint, we only use one slot to save space.
     static const unsigned THIS_VALUE_OR_SCOPE_SLOT = 1;
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -7414,16 +7414,27 @@ BaseCompiler::init()
           case ValType::I64:
             l.init(MIRType::Int64, pushLocal(8));
             break;
           default:
             MOZ_CRASH("Compiler bug: Unexpected local type");
         }
     }
 
+    if (debugInformation_) {
+        for (size_t i = 0; i < localInfo_.length(); i++) {
+            if (tlsSlot_ == i)
+                continue;
+            Local& l = localInfo_[i];
+            int32_t frameOffset = frameOffsetFromSlot(i, l.type());
+            if (!debugInformation_->vars().emplaceBack(l.type(), frameOffset))
+                return false;
+        }
+    }
+
     varHigh_ = localSize_;
 
     localSize_ = AlignBytes(localSize_, 16u);
 
     addInterruptCheck();
 
     return true;
 }
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -437,20 +437,17 @@ struct MetadataCacheablePod
 
     explicit MetadataCacheablePod(ModuleKind kind)
       : kind(kind),
         memoryUsage(MemoryUsage::None),
         minMemoryLength(0)
     {}
 };
 
-struct DebugTrapsData
-{
-    DebugTrapInfoVector breakpoints;
-};
+typedef Vector<VarInfoVector, 0, js::SystemAllocPolicy> VarInfoVectors;
 
 struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod
 {
     explicit Metadata(ModuleKind kind = ModuleKind::Wasm) : MetadataCacheablePod(kind) {}
     virtual ~Metadata() {}
 
     MetadataCacheablePod& pod() { return *this; }
     const MetadataCacheablePod& pod() const { return *this; }
@@ -466,16 +463,17 @@ struct Metadata : ShareableBase<Metadata
     CodeRangeVector       codeRanges;
     CallSiteVector        callSites;
     CallThunkVector       callThunks;
     NameInBytecodeVector  funcNames;
     CacheableChars        filename;
     bool                  debugEnabled;
     FuncDebugTrapsVector  funcDebugTraps;
     DebugTrapInfoVector   breakpoints;
+    VarInfoVectors        funcVars;
 
     bool usesMemory() const { return UsesMemory(memoryUsage); }
     bool hasSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
 
     const FuncExport& lookupFuncExport(uint32_t funcIndex) const;
 
     // AsmJSMetadata derives Metadata iff isAsmJS(). Mostly this distinction is
     // encapsulated within AsmJS.cpp, but the additional virtual functions allow
--- a/js/src/wasm/WasmDebugFrame.cpp
+++ b/js/src/wasm/WasmDebugFrame.cpp
@@ -56,18 +56,19 @@ DebugFrame::environmentChain()
         return cachedEnv_;
 
     JSContext* cx = instance()->cx();
     const CodeRange* range = instance()->code().lookupRange(pc_);
     MOZ_ASSERT(range);
     uint32_t funcIndex = range->funcIndex();
     Rooted<WasmInstanceObject*> object(cx, instance()->object());
 
+    uint8_t* fp = static_cast<uint8_t*>((void*)this) + sizeof(DebugFrame);
     JSAutoCompartment ac(cx, object);
-    cachedEnv_.init(WasmFunctionCallObject::create(cx, object, funcIndex));
+    cachedEnv_.init(WasmFunctionCallObject::create(cx, object, funcIndex, fp));
     return cachedEnv_;
 }
 
 bool
 DebugFrame::ensureObservingState(JSContext* cx, bool observing)
 {
    if (observing_ == observing)
        return true;
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -195,16 +195,18 @@ ModuleGenerator::init(UniqueModuleGenera
         MOZ_ASSERT(shared_->sigs.length() == MaxSigs);
         MOZ_ASSERT(shared_->tables.length() == MaxTables);
         MOZ_ASSERT(shared_->asmJSSigToTableIndex.length() == MaxSigs);
     }
 
     if (compileDebugInformation_) {
         if (!debugTraps_.resize(shared_->funcSigs.length()))
             return false;
+        if (!vars_.resize(shared_->funcSigs.length()))
+            return false;
     }
 
     return true;
 }
 
 bool
 ModuleGenerator::finishOutstandingTask()
 {
@@ -379,16 +381,19 @@ ModuleGenerator::finishTask(IonCompileTa
     FuncDebugInformation* maybeDebugInformation = task->maybeDebugInformation();
     if (maybeDebugInformation) {
         FuncDebugTraps& debugTraps = maybeDebugInformation->debugTraps();
         debugTraps.enterFrameTrap.offsetBy(offsetInWhole);
         debugTraps.leaveFrameTrap.offsetBy(offsetInWhole);
         debugTraps.farJumpOffset += offsetInWhole;
         debugTraps_[func.index()] = debugTraps;
 
+        VarInfoVector& vars = maybeDebugInformation->vars();
+        vars_[func.index()].swap(vars);
+
         DebugTrapInfoVector& breakpoints = maybeDebugInformation->breakpoints();
         for (auto p = breakpoints.begin(); p != breakpoints.end(); p++) {
             p->address.offsetBy(offsetInWhole);
             p->offset += func.lineOrBytecode();
         }
         if (!breakpoints_.append(breakpoints.begin(), breakpoints.end()))
             return false;
     }
@@ -1196,16 +1201,17 @@ ModuleGenerator::finish(const ShareableB
 
     // For asm.js, the tables vector is over-allocated (to avoid resize during
     // parallel copilation). Shrink it back down to fit.
     if (isAsmJS() && !metadata_->tables.resize(numTables_))
         return nullptr;
 
     if (compileDebugInformation_) {
        metadata_->funcDebugTraps.swap(debugTraps_);
+       metadata_->funcVars.swap(vars_);
     }
 
     // Assert CodeRanges are sorted.
 #ifdef DEBUG
     uint32_t lastEnd = 0;
     for (const CodeRange& codeRange : metadata_->codeRanges) {
         MOZ_ASSERT(codeRange.begin() >= lastEnd);
         lastEnd = codeRange.end();
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -88,16 +88,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     LinkData                        linkData_;
     MutableMetadata                 metadata_;
     ExportVector                    exports_;
     ImportVector                    imports_;
     DataSegmentVector               dataSegments_;
     ElemSegmentVector               elemSegments_;
     FuncDebugTrapsVector            debugTraps_;
     DebugTrapInfoVector             breakpoints_;
+    VarInfoVectors                  vars_;
 
     // Data scoped to the ModuleGenerator's lifetime
     UniqueModuleGeneratorData       shared_;
     uint32_t                        numSigs_;
     uint32_t                        numTables_;
     LifoAlloc                       lifo_;
     jit::JitContext                 jcx_;
     jit::TempAllocator              masmAlloc_;
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -834,16 +834,34 @@ Instance::ensureProfilingState(JSContext
             if (array[i])
                 UpdateEntry(*code_, newProfilingEnabled, &array[i]);
         }
     }
 
     return true;
 }
 
+uint32_t
+Instance::getVarsCount(JSContext* cx, uint32_t funcDefIndex)
+{
+    const VarInfoVector& vars = code().metadata().funcVars[funcDefIndex];
+    return vars.length();
+}
+
+bool
+Instance::getVarTypeAndOffset(JSContext* cx, uint32_t funcDefIndex, uint32_t varIndex, jit::MIRType* type, int32_t* offset)
+{
+    const VarInfoVector& vars = code().metadata().funcVars[funcDefIndex];
+    MOZ_ASSERT(varIndex < vars.length());
+    const VarInfo& varInfo = vars[varIndex];
+    *type = varInfo.type;
+    *offset = varInfo.offs;
+    return true;
+}
+
 void
 Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                         Metadata::SeenSet* seenMetadata,
                         ShareableBytes::SeenSet* seenBytes,
                         Table::SeenSet* seenTables,
                         size_t* code,
                         size_t* data) const
 {
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -122,16 +122,20 @@ class Instance
 
     void onMovingGrowMemory(uint8_t* prevMemoryBase);
     void onMovingGrowTable();
 
     // See Code::ensureProfilingState comment.
 
     MOZ_MUST_USE bool ensureProfilingState(JSContext* cx, bool enabled);
 
+    // Some debug utils.
+    uint32_t getVarsCount(JSContext* cx, uint32_t funcDefIndex);
+    bool getVarTypeAndOffset(JSContext* cx, uint32_t funcDefIndex, uint32_t varIndex, jit::MIRType* type, int32_t* offset);
+
     // about:memory reporting:
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf,
                        Metadata::SeenSet* seenMetadata,
                        ShareableBytes::SeenSet* seenBytes,
                        Table::SeenSet* seenTables,
                        size_t* code,
                        size_t* data) const;
--- a/js/src/wasm/WasmIonCompile.h
+++ b/js/src/wasm/WasmIonCompile.h
@@ -91,28 +91,31 @@ class FuncCompileResults
     jit::MacroAssembler& masm() { return masm_; }
     FuncOffsets& offsets() { return offsets_; }
 };
 
 class FuncDebugInformation
 {
     UniquePtr<FuncDebugTraps>      debugTraps_;
     UniquePtr<DebugTrapInfoVector> breakpoints_;
+    UniquePtr<VarInfoVector>       vars_;
 
     FuncDebugInformation(const FuncDebugInformation&) = delete;
     FuncDebugInformation& operator=(const FuncDebugInformation&) = delete;
 
   public:
     explicit FuncDebugInformation() {
         debugTraps_ = js::MakeUnique<FuncDebugTraps>();
         breakpoints_ = js::MakeUnique<DebugTrapInfoVector>();
+        vars_ = js::MakeUnique<VarInfoVector>();
     }
 
     FuncDebugTraps& debugTraps() { return *debugTraps_; }
     DebugTrapInfoVector& breakpoints() { return *breakpoints_; }
+    VarInfoVector& vars() { return *vars_; }
 };
 
 // An IonCompileTask represents the task of compiling a single function body. An
 // IonCompileTask is filled with the wasm code to be compiled on the main
 // validation thread, sent off to an Ion compilation helper thread which creates
 // the FuncCompileResults, and finally sent back to the validation thread. To
 // save time allocating and freeing memory, IonCompileTasks are reset() and
 // reused.
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -892,16 +892,26 @@ struct FuncDebugTraps
 
     FuncDebugTraps() : farJumpOffset(UNDEFINED_JUMP_OFFSET) {}
 
     bool undefined() const { return farJumpOffset == UNDEFINED_JUMP_OFFSET; }
 };
 
 WASM_DECLARE_POD_VECTOR(FuncDebugTraps, FuncDebugTrapsVector)
 
+struct VarInfo
+{
+    jit::MIRType  type;
+    int32_t offs;
+
+    VarInfo(jit::MIRType type_, int32_t offs_) : type(type_), offs(offs_) {}
+};
+
+WASM_DECLARE_POD_VECTOR(VarInfo, VarInfoVector)
+
 // A wasm::SymbolicAddress represents a pointer to a well-known function or
 // object that is embedded in wasm code. Since wasm code is serialized and
 // later deserialized into a different address space, symbolic addresses must be
 // used for *all* pointers into the address space. The MacroAssembler records a
 // list of all SymbolicAddresses and the offsets of their use in the code for
 // later patching during static linking.
 
 enum class SymbolicAddress