Fix global set ICs with branded global objects (bug 627486, r=cdleary).
☠☠ backed out by 11238fb4caf3 ☠ ☠
authorDavid Anderson <danderson@mozilla.com>
Tue, 01 Feb 2011 11:47:35 -0800
changeset 62036 54a515a97151625ed10eb2096546ee40bb3ce95e
parent 62035 01ab61f0066a3f16c6cbbd2d2ba465e5a551b550
child 62037 aa1cf1121a203aac671b85ba308510976a9cf454
child 62043 11238fb4caf3575eef1a3e5fbc823dcb55786db3
push idunknown
push userunknown
push dateunknown
reviewerscdleary
bugs627486
milestone2.0b11pre
Fix global set ICs with branded global objects (bug 627486, r=cdleary).
js/src/jit-test/tests/jaeger/bug627486.js
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/MonoIC.cpp
js/src/methodjit/MonoIC.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug627486.js
@@ -0,0 +1,23 @@
+// |jit-test| error: TypeError
+// vim: set ts=4 sw=4 tw=99 et:
+g = undefined;
+function L() { }
+
+function h() {
+    with (h) { }
+    for (var i = 0; i < 10; i++)
+        g();
+}
+
+function f(x) {
+    g = x;
+}
+
+f(L);
+h();
+f(L);
+f(2);
+h();
+
+/* Don't assert/crash. */
+
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -495,34 +495,43 @@ mjit::Compiler::finishThisUp(JITScript *
         cursor += sizeof(ic::MICInfo) * mics.length();
     } else {
         jit->mics = NULL;
     }
 
     if (ic::MICInfo *scriptMICs = jit->mics) {
         for (size_t i = 0; i < mics.length(); i++) {
             scriptMICs[i].kind = mics[i].kind;
-            scriptMICs[i].entry = fullCode.locationOf(mics[i].entry);
-            switch (mics[i].kind) {
-              case ic::MICInfo::GET:
-              case ic::MICInfo::SET:
-                if (mics[i].kind == ic::MICInfo::GET)
-                    scriptMICs[i].load = fullCode.locationOf(mics[i].load);
-                else
-                    scriptMICs[i].load = fullCode.locationOf(mics[i].store).labelAtOffset(0);
-                scriptMICs[i].shape = fullCode.locationOf(mics[i].shape);
-                scriptMICs[i].stubCall = stubCode.locationOf(mics[i].call);
-                scriptMICs[i].stubEntry = stubCode.locationOf(mics[i].stubEntry);
-                scriptMICs[i].u.name.typeConst = mics[i].u.name.typeConst;
-                scriptMICs[i].u.name.dataConst = mics[i].u.name.dataConst;
-                scriptMICs[i].u.name.usePropertyCache = mics[i].u.name.usePropertyCache;
-                break;
-              default:
-                JS_NOT_REACHED("Bad MIC kind");
+            scriptMICs[i].fastPathStart = fullCode.locationOf(mics[i].fastPathStart);
+            scriptMICs[i].slowPathStart = stubCode.locationOf(mics[i].slowPathStart);
+            if (mics[i].kind == ic::MICInfo::GET)
+                scriptMICs[i].load = fullCode.locationOf(mics[i].load);
+            else
+                scriptMICs[i].load = fullCode.locationOf(mics[i].store).labelAtOffset(0);
+            scriptMICs[i].shape = fullCode.locationOf(mics[i].shape);
+            scriptMICs[i].stubCall = stubCode.locationOf(mics[i].call);
+            scriptMICs[i].usePropertyCache = mics[i].usePropertyCache;
+            scriptMICs[i].extraShapeGuard = 0;
+            scriptMICs[i].objConst = mics[i].objConst;
+            scriptMICs[i].shapeReg = mics[i].shapeReg;
+            scriptMICs[i].objReg = mics[i].objReg;
+            scriptMICs[i].vr = mics[i].vr;
+
+            if (mics[i].kind == ic::MICInfo::SET) {
+                int offset = fullCode.locationOf(mics[i].shapeGuardJump) -
+                             scriptMICs[i].fastPathStart;
+                scriptMICs[i].inlineShapeJump = offset;
+                JS_ASSERT(scriptMICs[i].inlineShapeJump == offset);
+
+                offset = fullCode.locationOf(mics[i].fastPathRejoin) -
+                         scriptMICs[i].fastPathStart;
+                scriptMICs[i].fastRejoinOffset = offset;
+                JS_ASSERT(scriptMICs[i].fastRejoinOffset == offset);
             }
+
             stubCode.patch(mics[i].addrLabel, &scriptMICs[i]);
         }
     }
 
     jit->nCallICs = callICs.length();
     if (callICs.length()) {
         jit->callICs = (ic::CallICInfo *)cursor;
         cursor += sizeof(ic::CallICInfo) * callICs.length();
@@ -4303,17 +4312,17 @@ mjit::Compiler::jsop_getgname(uint32 ind
     FrameEntry *fe = frame.peek(-1);
     JS_ASSERT(fe->isTypeKnown() && fe->getKnownType() == JSVAL_TYPE_OBJECT);
 
     MICGenInfo mic(ic::MICInfo::GET);
     RESERVE_IC_SPACE(masm);
     RegisterID objReg;
     Jump shapeGuard;
 
-    mic.entry = masm.label();
+    mic.fastPathStart = masm.label();
     if (fe->isConstant()) {
         JSObject *obj = &fe->getValue().toObject();
         frame.pop();
         JS_ASSERT(obj->isNative());
 
         objReg = frame.allocReg();
 
         masm.load32FromImm(&obj->objShape, objReg);
@@ -4325,21 +4334,20 @@ mjit::Compiler::jsop_getgname(uint32 ind
         frame.pop();
         RegisterID reg = frame.allocReg();
 
         masm.loadShape(objReg, reg);
         shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, reg,
                                             Imm32(int32(JSObjectMap::INVALID_SHAPE)), mic.shape);
         frame.freeReg(reg);
     }
-    stubcc.linkExit(shapeGuard, Uses(0));
+    mic.slowPathStart = stubcc.linkExit(shapeGuard, Uses(0));
 
     stubcc.leave();
     passMICAddress(mic);
-    mic.stubEntry = stubcc.masm.label();
     mic.call = OOL_STUBCALL(ic::GetGlobalName);
 
     /* Garbage value. */
     uint32 slot = 1 << 24;
 
     masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
     Address address(objReg, slot);
     
@@ -4348,16 +4356,18 @@ mjit::Compiler::jsop_getgname(uint32 ind
     /* After dreg is loaded, it's safe to clobber objReg. */
     RegisterID dreg = objReg;
 
     mic.load = masm.loadValueWithAddressOffsetPatch(address, treg, dreg);
 
     frame.pushRegs(treg, dreg);
 
     stubcc.rejoin(Changes(1));
+
+    mic.fastPathRejoin = masm.label();
     mics.append(mic);
 
 #else
     jsop_getgname_slow(index);
 #endif
 }
 
 void
@@ -4373,101 +4383,81 @@ mjit::Compiler::jsop_setgname_slow(JSAto
     frame.pushSynced();
 }
 
 void
 mjit::Compiler::jsop_setgname(JSAtom *atom, bool usePropertyCache)
 {
 #if defined JS_MONOIC
     FrameEntry *objFe = frame.peek(-2);
+    FrameEntry *fe = frame.peek(-1);
     JS_ASSERT_IF(objFe->isTypeKnown(), objFe->getKnownType() == JSVAL_TYPE_OBJECT);
 
     MICGenInfo mic(ic::MICInfo::SET);
+    frame.pinEntry(fe, mic.vr);
+
     RESERVE_IC_SPACE(masm);
-    RegisterID objReg;
     Jump shapeGuard;
 
-    mic.entry = masm.label();
+    mic.fastPathStart = masm.label();
     if (objFe->isConstant()) {
         JSObject *obj = &objFe->getValue().toObject();
         JS_ASSERT(obj->isNative());
 
-        objReg = frame.allocReg();
-
-        masm.load32FromImm(&obj->objShape, objReg);
-        shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, objReg,
+        mic.objReg = frame.allocReg();
+        mic.shapeReg = mic.objReg;
+        mic.objConst = true;
+
+        masm.load32FromImm(&obj->objShape, mic.shapeReg);
+        shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, mic.shapeReg,
                                             Imm32(int32(JSObjectMap::INVALID_SHAPE)),
                                             mic.shape);
-        masm.move(ImmPtr(obj), objReg);
+        masm.move(ImmPtr(obj), mic.objReg);
     } else {
-        objReg = frame.copyDataIntoReg(objFe);
-        RegisterID reg = frame.allocReg();
-
-        masm.loadShape(objReg, reg);
-        shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, reg,
+        mic.objReg = frame.copyDataIntoReg(objFe);
+        mic.shapeReg = frame.allocReg();
+        mic.objConst = false;
+
+        masm.loadShape(mic.objReg, mic.shapeReg);
+        shapeGuard = masm.branch32WithPatch(Assembler::NotEqual, mic.shapeReg,
                                             Imm32(int32(JSObjectMap::INVALID_SHAPE)),
                                             mic.shape);
-        frame.freeReg(reg);
+        frame.freeReg(mic.shapeReg);
     }
-    stubcc.linkExit(shapeGuard, Uses(2));
+    mic.shapeGuardJump = shapeGuard;
+    mic.slowPathStart = stubcc.linkExit(shapeGuard, Uses(2));
 
     stubcc.leave();
     passMICAddress(mic);
-    mic.stubEntry = stubcc.masm.label();
     mic.call = OOL_STUBCALL(ic::SetGlobalName);
 
     /* Garbage value. */
     uint32 slot = 1 << 24;
 
-    /* Get both type and reg into registers. */
-    FrameEntry *fe = frame.peek(-1);
-
-    Value v;
-    RegisterID typeReg = Registers::ReturnReg;
-    RegisterID dataReg = Registers::ReturnReg;
-    JSValueType typeTag = JSVAL_TYPE_INT32;
-
-    mic.u.name.typeConst = fe->isTypeKnown();
-    mic.u.name.dataConst = fe->isConstant();
-    mic.u.name.usePropertyCache = usePropertyCache;
-
-    if (!mic.u.name.dataConst) {
-        dataReg = frame.ownRegForData(fe);
-        if (!mic.u.name.typeConst)
-            typeReg = frame.ownRegForType(fe);
-        else
-            typeTag = fe->getKnownType();
+    mic.usePropertyCache = usePropertyCache;
+
+    masm.loadPtr(Address(mic.objReg, offsetof(JSObject, slots)), mic.objReg);
+    Address address(mic.objReg, slot);
+
+    if (mic.vr.isConstant()) {
+        mic.store = masm.storeValueWithAddressOffsetPatch(mic.vr.value(), address);
+    } else if (mic.vr.isTypeKnown()) {
+        mic.store = masm.storeValueWithAddressOffsetPatch(ImmType(mic.vr.knownType()),
+                                                          mic.vr.dataReg(), address);
     } else {
-        v = fe->getValue();
+        mic.store = masm.storeValueWithAddressOffsetPatch(mic.vr.typeReg(), mic.vr.dataReg(), address);
     }
 
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
-    Address address(objReg, slot);
-
-    if (mic.u.name.dataConst) {
-        mic.store = masm.storeValueWithAddressOffsetPatch(v, address);
-    } else if (mic.u.name.typeConst) {
-        mic.store = masm.storeValueWithAddressOffsetPatch(ImmType(typeTag), dataReg, address);
-    } else {
-        mic.store = masm.storeValueWithAddressOffsetPatch(typeReg, dataReg, address);
-    }
-
-    frame.freeReg(objReg);
-    frame.popn(2);
-    if (mic.u.name.dataConst) {
-        frame.push(v);
-    } else {
-        if (mic.u.name.typeConst)
-            frame.pushTypedPayload(typeTag, dataReg);
-        else
-            frame.pushRegs(typeReg, dataReg);
-    }
+    frame.freeReg(mic.objReg);
+    frame.unpinEntry(mic.vr);
+    frame.shimmy(1);
 
     stubcc.rejoin(Changes(1));
 
+    mic.fastPathRejoin = masm.label();
     mics.append(mic);
 #else
     jsop_setgname_slow(atom, usePropertyCache);
 #endif
 }
 
 void
 mjit::Compiler::jsop_setelem_slow()
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -65,37 +65,31 @@ class Compiler : public BaseCompiler
         Jump jump;
         jsbytecode *pc;
     };
 
 #if defined JS_MONOIC
     struct MICGenInfo {
         MICGenInfo(ic::MICInfo::Kind kind) : kind(kind)
         { }
-        Label entry;
-        Label stubEntry;
+        Label fastPathStart;
+        Label slowPathStart;
+        Label fastPathRejoin;
+        Label load;
         DataLabel32 shape;
         DataLabelPtr addrLabel;
-        Label load;
         DataLabel32 store;
         Call call;
         ic::MICInfo::Kind kind;
-        jsbytecode *jumpTarget;
-        Jump traceHint;
-        MaybeJump slowTraceHint;
-        union {
-            struct {
-                bool typeConst;
-                bool dataConst;
-                bool usePropertyCache;
-            } name;
-            struct {
-                uint32 pcOffs;
-            } tracer;
-        } u;
+        Jump shapeGuardJump;
+        ValueRemat vr;
+        RegisterID objReg;
+        RegisterID shapeReg;
+        bool objConst;
+        bool usePropertyCache;
     };
 
     struct EqualityGenInfo {
         DataLabelPtr addrLabel;
         Label stubEntry;
         Call stubCall;
         BoolStub stub;
         MaybeJump jumpToStub;
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -43,16 +43,17 @@
 #include "StubCalls.h"
 #include "StubCalls-inl.h"
 #include "assembler/assembler/LinkBuffer.h"
 #include "assembler/assembler/MacroAssembler.h"
 #include "assembler/assembler/CodeLocation.h"
 #include "methodjit/CodeGenIncludes.h"
 #include "methodjit/Compiler.h"
 #include "methodjit/ICRepatcher.h"
+#include "methodjit/PolyIC.h"
 #include "InlineFrameAssembler.h"
 #include "jsobj.h"
 
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 
@@ -61,16 +62,18 @@ using namespace js::mjit;
 using namespace js::mjit::ic;
 
 typedef JSC::MacroAssembler::RegisterID RegisterID;
 typedef JSC::MacroAssembler::Address Address;
 typedef JSC::MacroAssembler::Jump Jump;
 typedef JSC::MacroAssembler::Imm32 Imm32;
 typedef JSC::MacroAssembler::ImmPtr ImmPtr;
 typedef JSC::MacroAssembler::Call Call;
+typedef JSC::MacroAssembler::Label Label;
+typedef JSC::MacroAssembler::DataLabel32 DataLabel32;
 
 #if defined JS_MONOIC
 
 static void
 PatchGetFallback(VMFrame &f, ic::MICInfo *ic)
 {
     Repatcher repatch(f.jit());
     JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::GetGlobalName));
@@ -93,18 +96,16 @@ ic::GetGlobalName(VMFrame &f, ic::MICInf
     {
         if (shape)
             PatchGetFallback(f, ic);
         stubs::GetGlobalName(f);
         return;
     }
     uint32 slot = shape->slot;
 
-    ic->u.name.touched = true;
-
     /* Patch shape guard. */
     Repatcher repatcher(f.jit());
     repatcher.repatch(ic->shape, obj->shape());
 
     /* Patch loads. */
     repatcher.patchAddressOffsetForValueLoad(ic->load, slot * sizeof(Value));
 
     /* Do load anyway... this time. */
@@ -136,61 +137,184 @@ template void JS_FASTCALL DisabledSetGlo
 template void JS_FASTCALL DisabledSetGlobalNoCache<false>(VMFrame &f, ic::MICInfo *ic);
 
 static void
 PatchSetFallback(VMFrame &f, ic::MICInfo *ic)
 {
     JSScript *script = f.fp()->script();
 
     Repatcher repatch(f.jit());
-    VoidStubMIC stub = ic->u.name.usePropertyCache
+    VoidStubMIC stub = ic->usePropertyCache
                        ? STRICT_VARIANT(DisabledSetGlobal)
                        : STRICT_VARIANT(DisabledSetGlobalNoCache);
     JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stub));
     repatch.relink(ic->stubCall, fptr);
 }
 
+static LookupStatus
+UpdateSetGlobalNameStub(VMFrame &f, ic::MICInfo *ic, JSObject *obj, const Shape *shape)
+{
+    Repatcher repatcher(ic->extraStub);
+
+    JSC::CodeLocationLabel label(JSC::MacroAssemblerCodePtr(ic->extraStub.start()));
+    repatcher.repatch(label.dataLabel32AtOffset(ic->extraShapeGuard), obj->shape());
+
+    label = label.labelAtOffset(ic->extraStoreOffset);
+    repatcher.patchAddressOffsetForValueStore(label, shape->slot * sizeof(Value),
+                                              ic->vr.isTypeKnown());
+
+    return Lookup_Cacheable;
+}
+
+static LookupStatus
+AttachSetGlobalNameStub(VMFrame &f, ic::MICInfo *ic, JSObject *obj, const Shape *shape)
+{
+    Assembler masm;
+
+    Label start = masm.label();
+
+    DataLabel32 shapeLabel;
+    Jump guard = masm.branch32WithPatch(Assembler::NotEqual, ic->shapeReg, Imm32(obj->shape()),
+                                        shapeLabel);
+
+    /* A constant object needs rematerialization. */
+    if (ic->objConst)
+        masm.move(ImmPtr(obj), ic->objReg);
+
+    JS_ASSERT(obj->branded());
+
+    /*
+     * Load obj->slots. If ic->objConst, then this clobbers objReg, because
+     * ic->objReg == ic->shapeReg.
+     */
+    masm.loadPtr(Address(ic->objReg, offsetof(JSObject, slots)), ic->shapeReg);
+
+    /* Test if overwriting a function-tagged slot. */
+    Address slot(ic->shapeReg, sizeof(Value) * shape->slot);
+    Jump isNotObject = masm.testObject(Assembler::NotEqual, slot);
+
+    /* Now, test if the object is a function object. */
+    masm.loadPayload(slot, ic->shapeReg);
+    Jump isFun = masm.testFunction(Assembler::Equal, ic->shapeReg);
+
+    /* Restore shapeReg to obj->slots, since we clobbered it. */
+    if (ic->objConst) {
+        masm.move(ImmPtr(obj), ic->objReg);
+        masm.loadPtr(Address(ic->objReg, offsetof(JSObject, slots)), ic->shapeReg);
+    }
+
+    /* If the object test fails, shapeReg is still obj->slots. */
+    isNotObject.linkTo(masm.label(), &masm);
+    DataLabel32 store = masm.storeValueWithAddressOffsetPatch(ic->vr, slot);
+
+    Jump done = masm.jump();
+
+    JITScript *jit = f.jit();
+    LinkerHelper linker(masm);
+    JSC::ExecutablePool *ep = linker.init(f.cx);
+    if (!ep)
+        return Lookup_Error;
+    if (!jit->execPools.append(ep)) {
+        ep->release();
+        js_ReportOutOfMemory(f.cx);
+        return Lookup_Error;
+    }
+
+    if (!linker.verifyRange(jit)) {
+        ep->release();
+        return Lookup_Uncacheable;
+    }
+
+    linker.link(done, ic->fastPathStart.labelAtOffset(ic->fastRejoinOffset));
+    linker.link(guard, ic->slowPathStart);
+    linker.link(isFun, ic->slowPathStart);
+
+    JSC::CodeLocationLabel cs = linker.finalize();
+    JaegerSpew(JSpew_PICs, "generated setgname stub at %p\n", cs.executableAddress());
+
+    Repatcher repatcher(f.jit());
+    repatcher.relink(ic->fastPathStart.jumpAtOffset(ic->inlineShapeJump), cs);
+
+    int offset = linker.locationOf(shapeLabel) - linker.locationOf(start);
+    ic->extraShapeGuard = offset;
+    JS_ASSERT(ic->extraShapeGuard == offset);
+    JS_ASSERT(offset);
+
+    ic->extraStub = JSC::JITCode(cs.executableAddress(), linker.size());
+    offset = linker.locationOf(store) - linker.locationOf(start);
+    ic->extraStoreOffset = offset;
+    JS_ASSERT(ic->extraStoreOffset == offset);
+
+    return Lookup_Cacheable;
+}
+
+static LookupStatus
+UpdateGlobalName(VMFrame &f, ic::MICInfo *ic, JSObject *obj, const Shape *shape)
+{
+    /* Give globals a chance to appear. */
+    if (!shape)
+        return Lookup_Uncacheable;
+
+    if (shape->isMethod() ||
+        !shape->hasDefaultSetter() ||
+        !shape->writable() ||
+        !shape->hasSlot())
+    {
+        /* Disable the IC for weird shape attributes. */
+        PatchSetFallback(f, ic);
+        return Lookup_Uncacheable;
+    }
+
+    /* Branded sets must guard that they don't overwrite method-valued properties. */
+    if (obj->branded()) {
+        /*
+         * If this slot has a function valued property, the tail of this opcode
+         * could change the shape. Even if it doesn't, the IC is probably
+         * pointless, because it will always hit the function-test path and
+         * bail out. In these cases, don't bother building or updating the IC.
+         */
+        const Value &v = obj->getSlot(shape->slot);
+        if (v.isObject() && v.toObject().isFunction()) {
+            /*
+             * If we're going to rebrand, the object may unbrand, allowing this
+             * IC to come back to life. In that case, we don't disable the IC.
+             */
+            if (!ChangesMethodValue(v, f.regs.sp[-1]))
+                PatchSetFallback(f, ic);
+            return Lookup_Uncacheable;
+        }
+
+        if (ic->extraShapeGuard)
+            return UpdateSetGlobalNameStub(f, ic, obj, shape);
+
+        return AttachSetGlobalNameStub(f, ic, obj, shape);
+    }
+
+    /* Object is not branded, so we can use the inline path. */
+    Repatcher repatcher(f.jit());
+    repatcher.repatch(ic->shape, obj->shape());
+    repatcher.patchAddressOffsetForValueStore(ic->load, shape->slot * sizeof(Value),
+                                              ic->vr.isTypeKnown());
+
+    return Lookup_Cacheable;
+}
+
 void JS_FASTCALL
 ic::SetGlobalName(VMFrame &f, ic::MICInfo *ic)
 {
     JSObject *obj = f.fp()->scopeChain().getGlobal();
     JSScript *script = f.fp()->script();
     JSAtom *atom = script->getAtom(GET_INDEX(f.regs.pc));
-    jsid id = ATOM_TO_JSID(atom);
-
-    JS_ASSERT(ic->kind == ic::MICInfo::SET);
+    const Shape *shape = obj->nativeLookup(ATOM_TO_JSID(atom));
 
-    const Shape *shape = obj->nativeLookup(id);
-    if (!shape ||
-        shape->isMethod() ||
-        !shape->hasDefaultSetter() ||
-        !shape->writable() ||
-        !shape->hasSlot())
-    {
-        if (shape)
-            PatchSetFallback(f, ic);
-        if (ic->u.name.usePropertyCache)
-            STRICT_VARIANT(stubs::SetGlobalName)(f, atom);
-        else
-            STRICT_VARIANT(stubs::SetGlobalNameNoCache)(f, atom);
-        return;
-    }
-    uint32 slot = shape->slot;
+    LookupStatus status = UpdateGlobalName(f, ic, obj, shape);
+    if (status == Lookup_Error)
+        THROW();
 
-    ic->u.name.touched = true;
-
-    /* Patch shape guard. */
-    Repatcher repatcher(f.jit());
-    repatcher.repatch(ic->shape, obj->shape());
-
-    /* Patch loads. */
-    repatcher.patchAddressOffsetForValueStore(ic->load, slot * sizeof(Value),
-                                              ic->u.name.typeConst);
-
-    if (ic->u.name.usePropertyCache)
+    if (ic->usePropertyCache)
         STRICT_VARIANT(stubs::SetGlobalName)(f, atom);
     else
         STRICT_VARIANT(stubs::SetGlobalNameNoCache)(f, atom);
 }
 
 class EqualityICLinker : public LinkerHelper
 {
     VMFrame &f;
@@ -1152,16 +1276,26 @@ JITScript::sweepCallICs(JSContext *cx, b
             JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic::Equality));
             repatcher.relink(ic.stubCall, fptr);
             repatcher.relink(ic.jumpToStub, ic.stubEntry);
 
             ic.generated = false;
             released++;
         }
 
+        for (uint32 i = 0; i < nMICs; i ++) {
+            ic::MICInfo &ic = mics[i];
+            if (!ic.extraShapeGuard)
+                continue;
+            JS_ASSERT(ic.kind == ic::MICInfo::SET);
+            repatcher.relink(ic.fastPathStart.jumpAtOffset(ic.inlineShapeJump), ic.slowPathStart);
+            ic.extraShapeGuard = 0;
+            released++;
+        }
+
         JS_ASSERT(released == execPools.length());
         for (uint32 i = 0; i < released; i++)
             execPools[i]->release();
         execPools.clear();
     }
 }
 
 void
--- a/js/src/methodjit/MonoIC.h
+++ b/js/src/methodjit/MonoIC.h
@@ -38,16 +38,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #if !defined jsjaeger_mono_ic_h__ && defined JS_METHODJIT && defined JS_MONOIC
 #define jsjaeger_mono_ic_h__
 
 #include "assembler/assembler/MacroAssembler.h"
 #include "assembler/assembler/CodeLocation.h"
+#include "assembler/moco/MocoStubs.h"
 #include "methodjit/MethodJIT.h"
 #include "CodeGenIncludes.h"
 
 namespace js {
 namespace mjit {
 
 class FrameSize
 {
@@ -95,44 +96,49 @@ struct MICInfo {
 #ifdef _MSC_VER
     : uint8_t
 #endif
     {
         GET,
         SET
     };
 
-    /* Used by multiple MICs. */
-    JSC::CodeLocationLabel entry;
-    JSC::CodeLocationLabel stubEntry;
+    typedef JSC::MacroAssembler::RegisterID RegisterID;
+
+    JSC::CodeLocationLabel fastPathStart;
+    JSC::CodeLocationLabel slowPathStart;
 
     /*
      * - ARM and x64 always emit exactly one instruction which needs to be
      *   patched. On ARM, the label points to the patched instruction, whilst
      *   on x64 it points to the instruction after it.
      * - For x86, the label "load" points to the start of the load/store
      *   sequence, which may consist of one or two "mov" instructions. Because
      *   of this, x86 is the only platform which requires non-trivial patching
      *   code.
      */
     JSC::CodeLocationLabel load;
     JSC::CodeLocationDataLabel32 shape;
     JSC::CodeLocationCall stubCall;
 
-    /* Used by all MICs. */
-    Kind kind : 3;
-    union {
-        /* Used by GET/SET. */
-        struct {
-            bool touched : 1;
-            bool typeConst : 1;
-            bool dataConst : 1;
-            bool usePropertyCache : 1;
-        } name;
-    } u;
+    /* SET only, if we had to generate an out-of-line path. */
+    Kind kind : 2;
+    bool usePropertyCache : 1;
+    int inlineShapeJump : 10;   /* Offset into inline path for shape jump. */
+    int extraShapeGuard : 6;    /* Offset into stub for shape guard. */
+    bool objConst : 1;          /* True if the object is constant. */
+    RegisterID objReg   : 5;    /* Register for object, if objConst is false. */
+    RegisterID shapeReg : 5;    /* Register for shape; volatile. */
+    JSC::JITCode extraStub;     /* Out-of-line generated stub. */
+
+    int fastRejoinOffset : 16;  /* Offset from fastPathStart to rejoin. */
+    int extraStoreOffset : 16;  /* Offset into store code. */
+
+    /* SET only. */
+    ValueRemat vr;              /* RHS value. */
 };
 
 struct TraceICInfo {
     TraceICInfo() {}
 
     JSC::CodeLocationLabel stubEntry;
     JSC::CodeLocationLabel jumpTarget;
     JSC::CodeLocationJump traceHint;