[JAEGER] Rewrite state reification for side exits.
authorDavid Anderson <danderson@mozilla.com>
Tue, 15 Jun 2010 01:40:39 -0700
changeset 52839 6a93a3de8e676b2854f267977105be5bcffd1409
parent 52838 a0aa7d6a99c6581258df99b9b779d860446bf5cd
child 52840 9dc3cfd5be060ca62d249d5a1f8bec48e850a435
push idunknown
push userunknown
push dateunknown
milestone1.9.3a5pre
[JAEGER] Rewrite state reification for side exits.
js/src/Makefile.in
js/src/methodjit/FrameEntry.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
js/src/methodjit/ImmutableSync.cpp
js/src/methodjit/ImmutableSync.h
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -308,16 +308,17 @@ CPPSRCS += 	Assertions.cpp \
 		BytecodeAnalyzer.cpp \
 		Logging.cpp \
 		StubCalls.cpp \
 		Compiler.cpp \
 		FrameState.cpp \
 		FastOps.cpp \
 		StubCompiler.cpp \
 		MonoIC.cpp \
+		ImmutableSync.cpp \
 		$(NULL)
 #		PICStubCompiler.cpp \
 
 ifeq (86, $(findstring 86,$(TARGET_CPU)))
 ifeq (x86_64, $(TARGET_CPU))
 ifeq ($(OS_ARCH),WINNT)
 ASFILES +=	TrampolineMasmX64.asm
 endif
--- a/js/src/methodjit/FrameEntry.h
+++ b/js/src/methodjit/FrameEntry.h
@@ -46,17 +46,17 @@
 #include "assembler/assembler/MacroAssembler.h"
 
 namespace js {
 namespace mjit {
 
 class FrameEntry
 {
     friend class FrameState;
-    friend struct SyncRegs;
+    friend class ImmutableSync;
 
   public:
     bool isConstant() const {
         return data.isConstant();
     }
 
     const jsval_layout &getConstant() const {
         JS_ASSERT(isConstant());
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -42,17 +42,17 @@
 
 using namespace js;
 using namespace js::mjit;
 
 /* Because of Value alignment */
 JS_STATIC_ASSERT(sizeof(FrameEntry) % 8 == 0);
 
 FrameState::FrameState(JSContext *cx, JSScript *script, Assembler &masm)
-  : cx(cx), script(script), masm(masm), entries(NULL)
+  : cx(cx), script(script), masm(masm), entries(NULL), reifier(cx, *this)
 {
 }
 
 FrameState::~FrameState()
 {
     cx->free(entries);
 }
 
@@ -69,16 +69,19 @@ FrameState::init(uint32 nargs)
 
     uint8 *cursor = (uint8 *)cx->malloc(sizeof(FrameEntry) * nslots +       // entries[]
                                         sizeof(FrameEntry *) * nslots +     // base[]
                                         sizeof(FrameEntry *) * nslots       // tracker.entries[]
                                         );
     if (!cursor)
         return false;
 
+    if (!reifier.init(nslots))
+        return false;
+
     entries = (FrameEntry *)cursor;
     cursor += sizeof(FrameEntry) * nslots;
 
     base = (FrameEntry **)cursor;
     args = base;
     locals = base + nargs;
     spBase = locals + script->nfixed;
     sp = spBase;
@@ -247,255 +250,29 @@ FrameState::assertValidRegisterState() c
             JS_ASSERT(regstate[fe->data.reg()].fe == fe);
         }
     }
 
     JS_ASSERT(checkedFreeRegs == freeRegs);
 }
 #endif
 
-namespace js {
-namespace mjit {
-
-struct SyncRegInfo {
-    FrameEntry *fe;
-    RematInfo::RematType type;
-    bool synced;
-};
-
-/*
- * While emitting sync code from the fast path to the slow path, we may get
- * a situation where a copy's backing store has no register. If we run into
- * this situation, the structure below is used to allocate registers.
- *
- * While walking the tracker, we remember all registers that have been sunk,
- * and can thus be clobbered. These are given out on a first-come, first-serve
- * basis. If none are available, one is evicted (explained later).
- *
- * After allocating a register, it is forced into the FrameEntry's RematInfo,
- * _despite_ this whole process being immutable with respect to the tracker.
- * This is so further copies of the FE can easily re-use the register.
- *
- * Once the backing store is reached (guaranteed to be after all copies,
- * thanks to the tracker ordering), the registers are placed back into the
- * free bucket. Here we necessarily undo the mucking from above. If the FE
- * never actually owned the register, we restore the fact that it was really
- * in memory.
- */
-struct SyncRegs {
-    typedef JSC::MacroAssembler::RegisterID RegisterID;
-
-    SyncRegs(const FrameState &frame, Assembler &masm, Registers avail)
-      : frame(frame), masm(masm), avail(avail)
-    {
-        memset(regs, 0, sizeof(regs));
-    }
-
-    bool shouldSyncData(FrameEntry *fe) {
-        if (fe->data.synced())
-            return false;
-        if (!fe->data.inRegister())
-            return true;
-        return !regs[fe->data.reg()].synced;
-    }
-
-    bool shouldSyncType(FrameEntry *fe) {
-        if (fe->type.synced())
-            return false;
-        if (!fe->type.inRegister())
-            return true;
-        return !regs[fe->type.reg()].synced;
-    }
-
-    void giveTypeReg(FrameEntry *fe) {
-        JS_ASSERT(fe->isCopied());
-        RegisterID reg = allocFor(fe, RematInfo::TYPE);
-        masm.loadTypeTag(frame.addressOf(fe), reg);
-        fe->type.setRegister(reg);
-    }
-
-    void giveDataReg(FrameEntry *fe) {
-        JS_ASSERT(fe->isCopied());
-        RegisterID reg = allocFor(fe, RematInfo::DATA);
-        masm.loadData32(frame.addressOf(fe), reg);
-        fe->data.setRegister(reg);
-    }
-
-    void forget(FrameEntry *fe) {
-        JS_ASSERT(!fe->isCopy());
-        if (fe->type.inRegister()) {
-            if (forgetReg(fe, RematInfo::TYPE, fe->type.reg()))
-                fe->type.setMemory();
-        }
-        if (fe->data.inRegister()) {
-            if (forgetReg(fe, RematInfo::DATA, fe->data.reg()))
-                fe->data.setMemory();
-        }
-    }
-
-  private:
-    RegisterID allocFor(FrameEntry *fe, RematInfo::RematType type) {
-        RegisterID reg;
-        if (!avail.empty())
-            reg = avail.takeAnyReg();
-        else
-            reg = evict();
-
-        regs[reg].fe = fe;
-        regs[reg].type = type;
-
-        return reg;
-    }
-
-    RegisterID evict() {
-        /*
-         * Worst case. This register is backed by a synced FE that might back
-         * copies. Evicting it could result in a load.
-         */
-        uint32 any = FrameState::InvalidIndex;
-        uint32 worst = FrameState::InvalidIndex;
-
-        /*
-         * Next best case. This register backs an unsynced FE on the fast
-         * path, and clobbering it will emit a store early.
-         */
-        uint32 nbest = FrameState::InvalidIndex;
-
-        for (uint32 i = 0; i < Assembler::TotalRegisters; i++) {
-            RegisterID reg = RegisterID(i);
-            if (!(Registers::maskReg(reg) & Registers::AvailRegs))
-                continue;
-
-            any = i;
-
-            FrameEntry *myFe = regs[reg].fe;
-            if (!myFe) {
-                FrameEntry *fe = frame.regstate[reg].fe;
-                if (!fe)
-                    continue;
-
-                if (!fe->isCopied())
-                    nbest = i;
-
-                if (frame.regstate[reg].type == RematInfo::TYPE && fe->type.synced())
-                    return reg;
-                else if (frame.regstate[reg].type == RematInfo::DATA && fe->data.synced())
-                    return reg;
-            } else {
-                worst = i;
-            }
-        }
-
-        /* We can early-sync another FE and grab its register. */
-        if (nbest != FrameState::InvalidIndex) {
-            FrameEntry *fe = frame.regstate[nbest].fe;
-            RematInfo::RematType type = frame.regstate[nbest].type;
-            if (type == RematInfo::TYPE)
-                frame.syncType(fe, frame.addressOf(fe), masm);
-            else
-                frame.syncData(fe, frame.addressOf(fe), masm);
-            regs[nbest].synced = true;
-            return RegisterID(nbest);
-        }
-
-        if (worst != FrameState::InvalidIndex) {
-            JS_ASSERT(regs[worst].fe);
-            if (regs[worst].type == RematInfo::TYPE)
-                regs[worst].fe->type.setMemory();
-            else
-                regs[worst].fe->data.setMemory();
-            regs[worst].fe = NULL;
-            return RegisterID(worst);
-        }
-
-        JS_NOT_REACHED("wat");
-
-        return RegisterID(worst);
-    }
-
-    /* Returns true if had an fe */
-    bool forgetReg(FrameEntry *checkFe, RematInfo::RematType type, RegisterID reg) {
-        /*
-         * Unchecked, because evict() can steal registers of synced FEs before
-         * the tracker has a chance to put them in the free list. This is
-         * harmless, and just avoids an assert.
-         */
-        avail.putRegUnchecked(reg);
-
-        /*
-         * It is necessary to check the type as well, for example an FE could
-         * have its type synced to EDI, and data unsynced. EDI could be
-         * evicted, but we only want to reset data, not type.
-         *
-         * The control flow could be better here, maybe this check should be
-         * in forgetRegs?
-         */
-        FrameEntry *fe = regs[reg].fe;
-        if (!fe || fe != checkFe || regs[reg].type != type)
-            return false;
-
-        regs[reg].fe = NULL;
-
-        return true;
-    }
-
-    const FrameState &frame;
-    Assembler &masm;
-    SyncRegInfo regs[Assembler::TotalRegisters];
-    Registers avail;
-};
-
-} } /* namespace js, mjit */
-
 void
 FrameState::syncFancy(Assembler &masm, Registers avail, uint32 resumeAt) const
 {
-    SyncRegs sr(*this, masm, avail);
+    /* :TODO: can be resumeAt? */
+    reifier.reset(&masm, avail, tracker.nentries);
 
     FrameEntry *tos = tosFe();
     for (uint32 i = resumeAt; i < tracker.nentries; i--) {
         FrameEntry *fe = tracker[i];
         if (fe >= tos)
             continue;
 
-        Address address = addressOf(fe);
-
-        if (!fe->isCopy()) {
-            sr.forget(fe);
-
-            if (sr.shouldSyncData(fe)) {
-                syncData(fe, address, masm);
-                if (fe->isConstant())
-                    continue;
-            }
-            if (sr.shouldSyncType(fe))
-                syncType(fe, addressOf(fe), masm);
-        } else {
-            FrameEntry *backing = fe->copyOf();
-            JS_ASSERT(backing != fe);
-            JS_ASSERT(!backing->isConstant() && !fe->isConstant());
-
-            if (!fe->type.synced()) {
-                /* :TODO: we can do better, the type is learned for all copies. */
-                if (fe->isTypeKnown()) {
-                    //JS_ASSERT(fe->getTypeTag() == backing->getTypeTag());
-                    masm.storeTypeTag(ImmTag(fe->getTypeTag()), address);
-                } else {
-                    if (!backing->type.inRegister())
-                        sr.giveTypeReg(backing);
-                    masm.storeTypeTag(backing->type.reg(), address);
-                }
-            }
-
-            if (!fe->data.synced()) {
-                if (!backing->data.inRegister())
-                    sr.giveDataReg(backing);
-                masm.storeData32(backing->data.reg(), address);
-            }
-        }
+        reifier.sync(fe);
     }
 }
 
 void
 FrameState::sync(Assembler &masm) const
 {
     /*
      * Keep track of free registers using a bitmask. If we have to drop into
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -39,16 +39,17 @@
 
 #if !defined jsjaeger_framestate_h__ && defined JS_METHODJIT
 #define jsjaeger_framestate_h__
 
 #include "jsapi.h"
 #include "methodjit/MachineRegs.h"
 #include "methodjit/FrameEntry.h"
 #include "CodeGenIncludes.h"
+#include "ImmutableSync.h"
 
 namespace js {
 namespace mjit {
 
 /*
  * The FrameState keeps track of values on the frame during compilation.
  * The compiler can query FrameState for information about arguments, locals,
  * and stack slots (all hereby referred to as "slots"). Slot information can
@@ -82,23 +83,23 @@ namespace mjit {
  * memory stores and clear the tracker.
  *
  * It is still possible to get really bad behavior with a very large script
  * that doesn't have branches or calls. That's okay, having this code in
  * minimizes damage and lets us introduce a hard cut-off point.
  */
 class FrameState
 {
+    friend class ImmutableSync;
+
     typedef JSC::MacroAssembler::RegisterID RegisterID;
     typedef JSC::MacroAssembler::Address Address;
     typedef JSC::MacroAssembler::Jump Jump;
     typedef JSC::MacroAssembler::Imm32 Imm32;
 
-    friend struct SyncRegs;
-
     static const uint32 InvalidIndex = 0xFFFFFFFF;
 
     struct Tracker {
         Tracker()
           : entries(NULL), nentries(0)
         { }
 
         void add(FrameEntry *fe) {
@@ -495,15 +496,17 @@ class FrameState
     /* Vector of tracked slot indexes. */
     Tracker tracker;
 
     /*
      * Register ownership state. This can't be used alone; to find whether an
      * entry is active, you must check the allocated registers.
      */
     RegisterState regstate[Assembler::TotalRegisters];
+
+    mutable ImmutableSync reifier;
 };
 
 } /* namespace mjit */
 } /* namespace js */
 
 #endif /* jsjaeger_framestate_h__ */
 
new file mode 100644
--- /dev/null
+++ b/js/src/methodjit/ImmutableSync.cpp
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=99:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
+ * May 28, 2008.
+ *
+ * The Initial Developer of the Original Code is
+ *   Brendan Eich <brendan@mozilla.org>
+ *
+ * Contributor(s):
+ *   David Anderson <danderson@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#include "FrameEntry.h"
+#include "FrameState.h"
+#include "FrameState-inl.h"
+#include "ImmutableSync.h"
+
+using namespace js;
+using namespace js::mjit;
+
+ImmutableSync::ImmutableSync(JSContext *cx, const FrameState &frame)
+  : cx(cx), entries(NULL), frame(frame)
+{
+}
+
+ImmutableSync::~ImmutableSync()
+{
+    cx->free(entries);
+}
+
+bool
+ImmutableSync::init(uint32 nentries)
+{
+    entries = (SyncEntry *)cx->malloc(sizeof(SyncEntry) * nentries);
+    return !!entries;
+}
+
+void
+ImmutableSync::reset(Assembler *masm, Registers avail, uint32 n)
+{
+    this->avail = avail;
+    this->nentries = n;
+    this->masm = masm;
+    memset(entries, 0, sizeof(SyncEntry) * nentries);
+    memset(regs, 0, sizeof(regs));
+}
+
+JSC::MacroAssembler::RegisterID
+ImmutableSync::allocReg()
+{
+    if (!avail.empty())
+        return avail.takeAnyReg();
+
+    uint32 lastResort = FrameState::InvalidIndex;
+    uint32 evictFromFrame = FrameState::InvalidIndex;
+
+    /* Find something to evict. */
+    for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) {
+        RegisterID reg = RegisterID(i);
+        if (!(Registers::maskReg(reg) & Registers::AvailRegs))
+            continue;
+
+        lastResort = 0;
+
+        if (!regs[i]) {
+            /* If the frame does not own this register, take it! */
+            FrameEntry *fe = frame.regstate[i].fe;
+            if (!fe)
+                return reg;
+
+            /*
+             * The Reifier does not own this register, but the frame does.
+             * This must mean that we've not yet processed this entry, and
+             * that it's data has not been clobbered.
+             */
+            JS_ASSERT(fe->trackerIndex() < nentries);
+
+            evictFromFrame = i;
+
+            /*
+             * If not copied, we can sync and not have to load again later.
+             * That's about as good as it gets, so just break out now.
+             */
+            if (!fe->isCopied())
+                break;
+        }
+    }
+
+    if (evictFromFrame != FrameState::InvalidIndex) {
+        FrameEntry *fe = frame.regstate[evictFromFrame].fe;
+        SyncEntry &e = entryFor(fe);
+        if (frame.regstate[evictFromFrame].type == RematInfo::TYPE) {
+            JS_ASSERT(!e.typeClobbered);
+            e.typeSynced = true;
+            e.typeClobbered = true;
+            masm->storeTypeTag(fe->type.reg(), frame.addressOf(fe));
+        } else {
+            JS_ASSERT(!e.dataClobbered);
+            e.dataSynced = true;
+            e.dataClobbered = true;
+            masm->storeData32(fe->data.reg(), frame.addressOf(fe));
+        }
+        return RegisterID(evictFromFrame);
+    }
+
+    JS_ASSERT(lastResort != FrameState::InvalidIndex);
+    JS_ASSERT(regs[lastResort]);
+
+    SyncEntry *e = regs[lastResort];
+    RegisterID reg = RegisterID(lastResort);
+    if (e->hasDataReg && e->dataReg == reg) {
+        e->hasDataReg = false;
+    } else if (e->hasTypeReg && e->typeReg == reg) {
+        e->hasTypeReg = false;
+    } else {
+        JS_NOT_REACHED("no way");
+    }
+
+    return reg;
+}
+
+inline ImmutableSync::SyncEntry &
+ImmutableSync::entryFor(FrameEntry *fe)
+{
+    JS_ASSERT(fe->trackerIndex() < nentries);
+    return entries[fe->trackerIndex()];
+}
+
+void
+ImmutableSync::sync(FrameEntry *fe)
+{
+    JS_ASSERT(nentries);
+    if (fe->isCopy())
+        syncCopy(fe);
+    else
+        syncNormal(fe);
+    nentries--;
+}
+
+JSC::MacroAssembler::RegisterID
+ImmutableSync::ensureTypeReg(FrameEntry *fe, SyncEntry &e)
+{
+    if (fe->type.inRegister() && !e.typeClobbered)
+        return fe->type.reg();
+    if (e.hasTypeReg)
+        return e.typeReg;
+    e.typeReg = allocReg();
+    e.hasTypeReg = true;
+    regs[e.typeReg] = &e;
+    masm->loadTypeTag(frame.addressOf(fe), e.typeReg);
+    return e.typeReg;
+}
+
+JSC::MacroAssembler::RegisterID
+ImmutableSync::ensureDataReg(FrameEntry *fe, SyncEntry &e)
+{
+    if (fe->data.inRegister() && !e.dataClobbered)
+        return fe->data.reg();
+    if (e.hasDataReg)
+        return e.dataReg;
+    e.dataReg = allocReg();
+    e.hasDataReg = true;
+    regs[e.dataReg] = &e;
+    masm->loadData32(frame.addressOf(fe), e.dataReg);
+    return e.dataReg;
+}
+
+void
+ImmutableSync::syncCopy(FrameEntry *fe)
+{
+    FrameEntry *backing = fe->copyOf();
+    SyncEntry &e = entryFor(backing);
+
+    JS_ASSERT(!backing->isConstant());
+
+    Address addr = frame.addressOf(fe);
+
+    if (fe->isTypeKnown() && !e.learnedType) {
+        e.learnedType = true;
+        e.typeTag = fe->getTypeTag();
+    }
+
+    if (!fe->data.synced())
+        masm->storeData32(ensureDataReg(backing, e), addr);
+
+    if (!fe->type.synced()) {
+        if (e.learnedType)
+            masm->storeTypeTag(ImmTag(e.typeTag), addr);
+        else
+            masm->storeTypeTag(ensureTypeReg(backing, e), addr);
+    }
+}
+
+void
+ImmutableSync::syncNormal(FrameEntry *fe)
+{
+    SyncEntry &e = entryFor(fe);
+
+    Address addr = frame.addressOf(fe);
+
+    if (fe->isTypeKnown()) {
+        e.learnedType = true;
+        e.typeTag = fe->getTypeTag();
+    }
+
+    if (!fe->data.synced() && !e.dataSynced) {
+        if (fe->isConstant()) {
+            masm->storeValue(fe->getValue(), addr);
+            return;
+        }
+        masm->storeData32(ensureDataReg(fe, e), addr);
+    }
+
+    if (!fe->type.synced() && !e.typeSynced) {
+        if (e.learnedType)
+            masm->storeTypeTag(ImmTag(e.typeTag), addr);
+        else
+            masm->storeTypeTag(ensureTypeReg(fe, e), addr);
+    }
+
+    if (e.hasDataReg) {
+        avail.putReg(e.dataReg);
+        regs[e.dataReg] = NULL;
+    } else if (!e.dataClobbered && fe->data.inRegister()) {
+        avail.putReg(fe->data.reg());
+    }
+
+    if (e.hasTypeReg) {
+        avail.putReg(e.typeReg);
+        regs[e.typeReg] = NULL;
+    } else if (!e.typeClobbered && fe->type.inRegister()) {
+        avail.putReg(fe->type.reg());
+    }
+}
+
new file mode 100644
--- /dev/null
+++ b/js/src/methodjit/ImmutableSync.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=99:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
+ * May 28, 2008.
+ *
+ * The Initial Developer of the Original Code is
+ *   Brendan Eich <brendan@mozilla.org>
+ *
+ * Contributor(s):
+ *   David Anderson <danderson@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#if !defined jsjaeger_imm_sync_h__ && defined JS_METHODJIT
+#define jsjaeger_imm_sync_h__
+
+#include "methodjit/MachineRegs.h"
+#include "methodjit/FrameEntry.h"
+#include "CodeGenIncludes.h"
+
+namespace js {
+namespace mjit {
+
+class FrameState;
+
+/*
+ * This is a structure nestled within the FrameState used for safely syncing
+ * registers to memory during transitions from the fast path into a slow path
+ * stub call. During this process, the frame itself is immutable, and we may
+ * run out of registers needed to remat copies.
+ *
+ * This structure maintains a mapping of the tracker used to perform ad-hoc
+ * register allocation.
+ */
+class ImmutableSync
+{
+    typedef JSC::MacroAssembler::RegisterID RegisterID;
+    typedef JSC::MacroAssembler::Address Address;
+
+    struct SyncEntry {
+        /*
+         * NB: clobbered and sync mean the same thing: the register associated
+         * in the FrameEntry is no longer valid, and has been written back.
+         *
+         * They are separated for readability.
+         */
+        bool dataSynced;
+        bool typeSynced;
+        bool dataClobbered;
+        bool typeClobbered;
+        RegisterID dataReg;
+        RegisterID typeReg;
+        bool hasDataReg;
+        bool hasTypeReg;
+        bool learnedType;
+        JSValueMask32 typeTag;
+    };
+
+  public:
+    ImmutableSync(JSContext *cx, const FrameState &frame);
+    ~ImmutableSync();
+    bool init(uint32 nentries);
+
+    void reset(Assembler *masm, Registers avail, uint32 n);
+    void sync(FrameEntry *fe);
+
+  private:
+    void syncCopy(FrameEntry *fe);
+    void syncNormal(FrameEntry *fe);
+    RegisterID ensureDataReg(FrameEntry *fe, SyncEntry &e);
+    RegisterID ensureTypeReg(FrameEntry *fe, SyncEntry &e);
+    RegisterID allocReg();
+
+    inline SyncEntry &entryFor(FrameEntry *fe);
+
+  private:
+    JSContext *cx;
+    SyncEntry *entries;
+    const FrameState &frame;
+    uint32 nentries;
+    Registers avail;
+    Assembler *masm;
+    SyncEntry *regs[Assembler::TotalRegisters];
+};
+
+} /* namespace mjit */
+} /* namespace js */
+
+#endif /* jsjaeger_imm_sync_h__ */
+