Bug 1013001 - Make it simpler to deal with nursery pointers in the compiler. r=jandem, a=lsblakk
authorTerrence Cole <terrence@mozilla.com>
Wed, 15 Oct 2014 14:26:52 -0700
changeset 225850 c94fc6b83daa
parent 225849 f7483e854a43
child 225851 f953384743a4
push id4039
push userryanvm@gmail.com
push date2014-10-29 14:09 +0000
treeherdermozilla-beta@c94fc6b83daa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, lsblakk
bugs1013001
milestone34.0
Bug 1013001 - Make it simpler to deal with nursery pointers in the compiler. r=jandem, a=lsblakk
js/src/jit/IonCaches.cpp
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/shared/Assembler-shared.h
js/src/jit/shared/Assembler-x86-shared.h
js/src/jit/x64/MacroAssembler-x64.h
js/src/jit/x86/Assembler-x86.h
js/src/jit/x86/MacroAssembler-x86.h
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -472,29 +472,29 @@ GeneratePrototypeGuards(JSContext *cx, I
      */
     JS_ASSERT(obj != holder);
 
     if (obj->hasUncacheableProto()) {
         // Note: objectReg and scratchReg may be the same register, so we cannot
         // use objectReg in the rest of this function.
         masm.loadPtr(Address(objectReg, JSObject::offsetOfType()), scratchReg);
         Address proto(scratchReg, types::TypeObject::offsetOfProto());
-        masm.branchNurseryPtr(Assembler::NotEqual, proto,
-                              ImmMaybeNurseryPtr(obj->getProto()), failures);
+        masm.branchPtr(Assembler::NotEqual, proto,
+                       ImmMaybeNurseryPtr(obj->getProto()), failures);
     }
 
     JSObject *pobj = IsCacheableDOMProxy(obj)
                      ? obj->getTaggedProto().toObjectOrNull()
                      : obj->getProto();
     if (!pobj)
         return;
     while (pobj != holder) {
         if (pobj->hasUncacheableProto()) {
             JS_ASSERT(!pobj->hasSingletonType());
-            masm.moveNurseryPtr(ImmMaybeNurseryPtr(pobj), scratchReg);
+            masm.movePtr(ImmMaybeNurseryPtr(pobj), scratchReg);
             Address objType(scratchReg, JSObject::offsetOfType());
             masm.branchPtr(Assembler::NotEqual, objType, ImmGCPtr(pobj->type()), failures);
         }
         pobj = pobj->getProto();
     }
 }
 
 // Note: This differs from IsCacheableProtoChain in BaselineIC.cpp in that
@@ -804,17 +804,17 @@ GenerateReadSlot(JSContext *cx, IonScrip
     if (obj != holder) {
         // Note: this may clobber the object register if it's used as scratch.
         GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg,
                                 &prototypeFailures);
 
         if (holder) {
             // Guard on the holder's shape.
             holderReg = scratchReg;
-            masm.moveNurseryPtr(ImmMaybeNurseryPtr(holder), holderReg);
+            masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg);
             masm.branchPtr(Assembler::NotEqual,
                            Address(holderReg, JSObject::offsetOfShape()),
                            ImmGCPtr(holder->lastProperty()),
                            &prototypeFailures);
         } else {
             // The property does not exist. Guard on everything in the
             // prototype chain.
             JSObject *proto = obj->getTaggedProto().toObjectOrNull();
@@ -1018,17 +1018,17 @@ GenerateCallGetter(JSContext *cx, IonScr
     }
 
     // Note: this may clobber the object register if it's used as scratch.
     if (obj != holder)
         GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, failures);
 
     // Guard on the holder's shape.
     Register holderReg = scratchReg;
-    masm.moveNurseryPtr(ImmMaybeNurseryPtr(holder), holderReg);
+    masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg);
     masm.branchPtr(Assembler::NotEqual,
                    Address(holderReg, JSObject::offsetOfShape()),
                    ImmGCPtr(holder->lastProperty()),
                    maybePopAndFail);
 
     if (spillObjReg)
         masm.pop(object);
 
@@ -1492,17 +1492,17 @@ GetPropertyIC::tryAttachDOMProxyUnshadow
         // getprop.
         Register scratchReg = output().valueReg().scratchReg();
         GeneratePrototypeGuards(cx, ion, masm, obj, holder, object(), scratchReg, &failures);
 
         // Rename scratch for clarity.
         Register holderReg = scratchReg;
 
         // Guard on the holder of the property
-        masm.moveNurseryPtr(ImmMaybeNurseryPtr(holder), holderReg);
+        masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg);
         masm.branchPtr(Assembler::NotEqual,
                     Address(holderReg, JSObject::offsetOfShape()),
                     ImmGCPtr(holder->lastProperty()),
                     &failures);
 
         if (canCache == CanAttachReadSlot) {
             EmitLoadSlot(masm, holder, shape, holderReg, output(), scratchReg);
         } else {
@@ -2270,17 +2270,17 @@ GenerateCallSetter(JSContext *cx, IonScr
 
         Label protoFailure;
         Label protoSuccess;
 
         // Generate prototype/shape guards.
         if (obj != holder)
             GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, &protoFailure);
 
-        masm.moveNurseryPtr(ImmMaybeNurseryPtr(holder), scratchReg);
+        masm.movePtr(ImmMaybeNurseryPtr(holder), scratchReg);
         masm.branchPtr(Assembler::NotEqual,
                        Address(scratchReg, JSObject::offsetOfShape()),
                        ImmGCPtr(holder->lastProperty()),
                        &protoFailure);
 
         masm.jump(&protoSuccess);
 
         masm.bind(&protoFailure);
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -246,37 +246,16 @@ template void MacroAssembler::guardObjec
 template void MacroAssembler::guardObjectType(Register obj, const TypeWrapper *types,
                                               Register scratch, Label *miss);
 
 template void MacroAssembler::guardType(const Address &address, types::Type type,
                                         Register scratch, Label *miss);
 template void MacroAssembler::guardType(const ValueOperand &value, types::Type type,
                                         Register scratch, Label *miss);
 
-void
-MacroAssembler::branchNurseryPtr(Condition cond, const Address &ptr1, ImmMaybeNurseryPtr ptr2,
-                                 Label *label)
-{
-#ifdef JSGC_GENERATIONAL
-    if (ptr2.value && gc::IsInsideNursery(ptr2.value))
-        embedsNurseryPointers_ = true;
-#endif
-    branchPtr(cond, ptr1, ptr2, label);
-}
-
-void
-MacroAssembler::moveNurseryPtr(ImmMaybeNurseryPtr ptr, Register reg)
-{
-#ifdef JSGC_GENERATIONAL
-    if (ptr.value && gc::IsInsideNursery(ptr.value))
-        embedsNurseryPointers_ = true;
-#endif
-    movePtr(ptr, reg);
-}
-
 template<typename S, typename T>
 static void
 StoreToTypedFloatArray(MacroAssembler &masm, int arrayType, const S &value, const T &dest)
 {
     switch (arrayType) {
       case Scalar::Float32:
         masm.storeFloat32(value, dest);
         break;
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -178,17 +178,16 @@ class MacroAssembler : public MacroAssem
             JS_ASSERT(isInitialized());
             masm.branchPtr(cond(), reg(), ptr_, jump());
         }
     };
 
     mozilla::Maybe<AutoRooter> autoRooter_;
     mozilla::Maybe<IonContext> ionContext_;
     mozilla::Maybe<AutoIonContextAlloc> alloc_;
-    bool embedsNurseryPointers_;
 
     // SPS instrumentation, only used for Ion caches.
     mozilla::Maybe<IonInstrumentation> spsInstrumentation_;
     jsbytecode *spsPc_;
 
   private:
     // This field is used to manage profiling instrumentation output. If
     // provided and enabled, then instrumentation will be emitted around call
@@ -201,18 +200,17 @@ class MacroAssembler : public MacroAssem
     NonAssertingLabel sequentialFailureLabel_;
     NonAssertingLabel parallelFailureLabel_;
 
   public:
     // If instrumentation should be emitted, then the sps parameter should be
     // provided, but otherwise it can be safely omitted to prevent all
     // instrumentation from being emitted.
     MacroAssembler()
-      : embedsNurseryPointers_(false),
-        sps_(nullptr)
+      : sps_(nullptr)
     {
         IonContext *icx = GetIonContext();
         JSContext *cx = icx->cx;
         if (cx)
             constructRoot(cx);
 
         if (!icx->temp) {
             JS_ASSERT(cx);
@@ -225,18 +223,17 @@ class MacroAssembler : public MacroAssem
         m_buffer.id = icx->getNextAssemblerId();
 #endif
     }
 
     // This constructor should only be used when there is no IonContext active
     // (for example, Trampoline-$(ARCH).cpp and IonCaches.cpp).
     explicit MacroAssembler(JSContext *cx, IonScript *ion = nullptr,
                             JSScript *script = nullptr, jsbytecode *pc = nullptr)
-      : embedsNurseryPointers_(false),
-        sps_(nullptr)
+      : sps_(nullptr)
     {
         constructRoot(cx);
         ionContext_.emplace(cx, (js::jit::TempAllocator *)nullptr);
         alloc_.emplace(cx);
         moveResolver_.setAllocator(*ionContext_->temp);
 #ifdef JS_CODEGEN_ARM
         initWithAllocator();
         m_buffer.id = GetIonContext()->getNextAssemblerId();
@@ -252,18 +249,17 @@ class MacroAssembler : public MacroAssem
                 sps_->setPushed(script);
             }
         }
     }
 
     // asm.js compilation handles its own IonContext-pushing
     struct AsmJSToken {};
     explicit MacroAssembler(AsmJSToken)
-      : embedsNurseryPointers_(false),
-        sps_(nullptr)
+      : sps_(nullptr)
     {
 #ifdef JS_CODEGEN_ARM
         initWithAllocator();
         m_buffer.id = 0;
 #endif
     }
 
     void setInstrumentation(IonInstrumentation *sps) {
@@ -283,20 +279,16 @@ class MacroAssembler : public MacroAssem
     MoveResolver &moveResolver() {
         return moveResolver_;
     }
 
     size_t instructionsSize() const {
         return size();
     }
 
-    bool embedsNurseryPointers() const {
-        return embedsNurseryPointers_;
-    }
-
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source, typename TypeSet>
     void guardTypeSet(const Source &address, const TypeSet *types, BarrierKind kind, Register scratch, Label *miss);
     template <typename TypeSet>
     void guardObjectType(Register obj, const TypeSet *types, Register scratch, Label *miss);
     template <typename Source>
     void guardType(const Source &address, types::Type type, Register scratch, Label *miss);
@@ -684,20 +676,16 @@ class MacroAssembler : public MacroAssem
 
         callPreBarrier(address, type);
         jump(&done);
 
         align(8);
         bind(&done);
     }
 
-    void branchNurseryPtr(Condition cond, const Address &ptr1, ImmMaybeNurseryPtr ptr2,
-                          Label *label);
-    void moveNurseryPtr(ImmMaybeNurseryPtr ptr, Register reg);
-
     void canonicalizeDouble(FloatRegister reg) {
         Label notNaN;
         branchDouble(DoubleOrdered, reg, reg, &notNaN);
         loadConstantDouble(JS::GenericNaN(), reg);
         bind(&notNaN);
     }
 
     void canonicalizeFloat(FloatRegister reg) {
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2107,16 +2107,21 @@ MacroAssemblerARMCompat::movePtr(ImmWord
     ma_mov(Imm32(imm.value), dest);
 }
 void
 MacroAssemblerARMCompat::movePtr(ImmGCPtr imm, Register dest)
 {
     ma_mov(imm, dest);
 }
 void
+MacroAssemblerARMCompat::movePtr(ImmMaybeNurseryPtr imm, Register dest)
+{
+    movePtr(noteMaybeNurseryPtr(imm), dest);
+}
+void
 MacroAssemblerARMCompat::movePtr(ImmPtr imm, Register dest)
 {
     movePtr(ImmWord(uintptr_t(imm.value)), dest);
 }
 void
 MacroAssemblerARMCompat::movePtr(AsmJSImmPtr imm, Register dest)
 {
     RelocStyle rs;
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -616,16 +616,19 @@ class MacroAssemblerARMCompat : public M
     }
     void push(ImmWord imm) {
         push(Imm32(imm.value));
     }
     void push(ImmGCPtr imm) {
         ma_mov(imm, ScratchRegister);
         ma_push(ScratchRegister);
     }
+    void push(ImmMaybeNurseryPtr imm) {
+        push(noteMaybeNurseryPtr(imm));
+    }
     void push(const Address &address) {
         ma_ldr(Operand(address.base, address.offset), ScratchRegister);
         ma_push(ScratchRegister);
     }
     void push(Register reg) {
         ma_push(reg);
     }
     void push(FloatRegister reg) {
@@ -1034,16 +1037,19 @@ class MacroAssemblerARMCompat : public M
         ma_cmp(secondScratchReg_, ptr);
         return jumpWithPatch(label, cond);
     }
     void branchPtr(Condition cond, Address addr, ImmGCPtr ptr, Label *label) {
         ma_ldr(addr, secondScratchReg_);
         ma_cmp(secondScratchReg_, ptr);
         ma_b(label, cond);
     }
+    void branchPtr(Condition cond, Address addr, ImmMaybeNurseryPtr ptr, Label *label) {
+        branchPtr(cond, addr, noteMaybeNurseryPtr(ptr), label);
+    }
     void branchPtr(Condition cond, Address addr, ImmWord ptr, Label *label) {
         ma_ldr(addr, secondScratchReg_);
         ma_cmp(secondScratchReg_, ptr);
         ma_b(label, cond);
     }
     void branchPtr(Condition cond, Address addr, ImmPtr ptr, Label *label) {
         branchPtr(cond, addr, ImmWord(uintptr_t(ptr.value)), label);
     }
@@ -1156,17 +1162,17 @@ class MacroAssemblerARMCompat : public M
     void tagValue(JSValueType type, Register payload, ValueOperand dest);
 
     void pushValue(ValueOperand val);
     void popValue(ValueOperand val);
     void pushValue(const Value &val) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         push(Imm32(jv.s.tag));
         if (val.isMarkable())
-            push(ImmGCPtr(reinterpret_cast<gc::Cell *>(val.toGCThing())));
+            push(ImmMaybeNurseryPtr(reinterpret_cast<gc::Cell *>(val.toGCThing())));
         else
             push(Imm32(jv.s.payload.i32));
     }
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         ma_push(reg);
     }
     void pushValue(const Address &addr);
@@ -1307,16 +1313,17 @@ class MacroAssemblerARMCompat : public M
     void move32(Imm32 imm, Register dest);
     void move32(Register src, Register dest);
 
     void movePtr(Register src, Register dest);
     void movePtr(ImmWord imm, Register dest);
     void movePtr(ImmPtr imm, Register dest);
     void movePtr(AsmJSImmPtr imm, Register dest);
     void movePtr(ImmGCPtr imm, Register dest);
+    void movePtr(ImmMaybeNurseryPtr imm, Register dest);
 
     void load8SignExtend(const Address &address, Register dest);
     void load8SignExtend(const BaseIndex &src, Register dest);
 
     void load8ZeroExtend(const Address &address, Register dest);
     void load8ZeroExtend(const BaseIndex &src, Register dest);
 
     void load16SignExtend(const Address &address, Register dest);
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -193,41 +193,60 @@ struct PatchedImmPtr {
     explicit PatchedImmPtr()
       : value(nullptr)
     { }
     explicit PatchedImmPtr(const void *value)
       : value(const_cast<void*>(value))
     { }
 };
 
+class AssemblerShared;
+class ImmGCPtr;
+
+// Used for immediates which require relocation and may be traced during minor GC.
+class ImmMaybeNurseryPtr
+{
+    friend class AssemblerShared;
+    friend class ImmGCPtr;
+    const gc::Cell *value;
+
+    ImmMaybeNurseryPtr() : value(0) {}
+
+  public:
+    explicit ImmMaybeNurseryPtr(const gc::Cell *ptr) : value(ptr)
+    {
+        MOZ_ASSERT(!IsPoisonedPtr(ptr));
+
+        // asm.js shouldn't be creating GC things
+        MOZ_ASSERT(!IsCompilingAsmJS());
+    }
+};
+
 // Used for immediates which require relocation.
-struct ImmGCPtr
+class ImmGCPtr
 {
+  public:
     const gc::Cell *value;
 
     explicit ImmGCPtr(const gc::Cell *ptr) : value(ptr)
     {
         JS_ASSERT(!IsPoisonedPtr(ptr));
         JS_ASSERT_IF(ptr, ptr->isTenured());
 
         // asm.js shouldn't be creating GC things
         JS_ASSERT(!IsCompilingAsmJS());
     }
 
-  protected:
+  private:
     ImmGCPtr() : value(0) {}
-};
 
-// Used for immediates which require relocation and may be traced during minor GC.
-struct ImmMaybeNurseryPtr : public ImmGCPtr
-{
-    explicit ImmMaybeNurseryPtr(gc::Cell *ptr)
+    friend class AssemblerShared;
+    explicit ImmGCPtr(ImmMaybeNurseryPtr ptr) : value(ptr.value)
     {
-        this->value = ptr;
-        JS_ASSERT(!IsPoisonedPtr(ptr));
+        JS_ASSERT(!IsPoisonedPtr(ptr.value));
 
         // asm.js shouldn't be creating GC things
         JS_ASSERT(!IsCompilingAsmJS());
     }
 };
 
 // Pointer to be embedded as an immediate that is loaded/stored from by an
 // instruction.
@@ -828,30 +847,49 @@ class AssemblerShared
 {
     Vector<CallSite, 0, SystemAllocPolicy> callsites_;
     Vector<AsmJSHeapAccess, 0, SystemAllocPolicy> asmJSHeapAccesses_;
     Vector<AsmJSGlobalAccess, 0, SystemAllocPolicy> asmJSGlobalAccesses_;
     Vector<AsmJSAbsoluteLink, 0, SystemAllocPolicy> asmJSAbsoluteLinks_;
 
   protected:
     bool enoughMemory_;
+    bool embedsNurseryPointers_;
 
   public:
     AssemblerShared()
-     : enoughMemory_(true)
+     : enoughMemory_(true),
+       embedsNurseryPointers_(false)
     {}
 
     void propagateOOM(bool success) {
         enoughMemory_ &= success;
     }
 
     bool oom() const {
         return !enoughMemory_;
     }
 
+    bool embedsNurseryPointers() const {
+        return embedsNurseryPointers_;
+    }
+
+    ImmGCPtr noteMaybeNurseryPtr(ImmMaybeNurseryPtr ptr) {
+#ifdef JSGC_GENERATIONAL
+        if (ptr.value && gc::IsInsideNursery(ptr.value)) {
+            // FIXME: Ideally we'd assert this in all cases, but PJS needs to
+            //        compile IC's from off-main-thread; it will not touch
+            //        nursery pointers, however.
+            MOZ_ASSERT(GetIonContext()->runtime->onMainThread());
+            embedsNurseryPointers_ = true;
+        }
+#endif
+        return ImmGCPtr(ptr);
+    }
+
     void append(const CallSiteDesc &desc, size_t currentOffset, size_t framePushed) {
         // framePushed does not include sizeof(AsmJSFrame), so add it in here (see
         // CallSite::stackDepth).
         CallSite callsite(desc, currentOffset, framePushed + sizeof(AsmJSFrame));
         enoughMemory_ &= callsites_.append(callsite);
     }
     CallSiteVector &&extractCallSites() { return Move(callsites_); }
 
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -191,22 +191,16 @@ class AssemblerX86Shared : public Assemb
     };
 
     Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_;
     Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
     CompactBufferWriter jumpRelocations_;
     CompactBufferWriter dataRelocations_;
     CompactBufferWriter preBarriers_;
 
-    void writeDataRelocation(const Value &val) {
-        if (val.isMarkable()) {
-            JS_ASSERT(static_cast<gc::Cell*>(val.toGCThing())->isTenured());
-            dataRelocations_.writeUnsigned(masm.currentOffset());
-        }
-    }
     void writeDataRelocation(ImmGCPtr ptr) {
         if (ptr.value)
             dataRelocations_.writeUnsigned(masm.currentOffset());
     }
     void writePrebarrierOffset(CodeOffsetLabel label) {
         preBarriers_.writeUnsigned(label.offset());
     }
 
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -106,16 +106,24 @@ class MacroAssemblerX64 : public MacroAs
     /////////////////////////////////////////////////////////////////
     void call(ImmWord target) {
         mov(target, rax);
         call(rax);
     }
     void call(ImmPtr target) {
         call(ImmWord(uintptr_t(target.value)));
     }
+    void writeDataRelocation(const Value &val) {
+        if (val.isMarkable()) {
+            gc::Cell *cell = reinterpret_cast<gc::Cell *>(val.toGCThing());
+            if (cell && gc::IsInsideNursery(cell))
+                embedsNurseryPointers_ = true;
+            dataRelocations_.writeUnsigned(masm.currentOffset());
+        }
+    }
 
     // Refers to the upper 32 bits of a 64-bit Value operand.
     // On x86_64, the upper 32 bits do not necessarily only contain the type.
     Operand ToUpper32(Operand base) {
         switch (base.kind()) {
           case Operand::MEM_REG_DISP:
             return Operand(Register::FromCode(base.base()), base.disp() + 4);
 
@@ -489,16 +497,19 @@ class MacroAssemblerX64 : public MacroAs
     void cmpPtr(Register lhs, const Imm32 rhs) {
         cmpq(lhs, rhs);
     }
     void cmpPtr(const Operand &lhs, const ImmGCPtr rhs) {
         JS_ASSERT(!lhs.containsReg(ScratchReg));
         movq(rhs, ScratchReg);
         cmpq(lhs, ScratchReg);
     }
+    void cmpPtr(const Operand &lhs, const ImmMaybeNurseryPtr rhs) {
+        cmpPtr(lhs, noteMaybeNurseryPtr(rhs));
+    }
     void cmpPtr(const Operand &lhs, const ImmWord rhs) {
         if ((intptr_t)rhs.value <= INT32_MAX && (intptr_t)rhs.value >= INT32_MIN) {
             cmpq(lhs, Imm32((int32_t)rhs.value));
         } else {
             mov(rhs, ScratchReg);
             cmpq(lhs, ScratchReg);
         }
     }
@@ -710,16 +721,19 @@ class MacroAssemblerX64 : public MacroAs
         mov(imm, dest);
     }
     void movePtr(AsmJSImmPtr imm, Register dest) {
         mov(imm, dest);
     }
     void movePtr(ImmGCPtr imm, Register dest) {
         movq(imm, dest);
     }
+    void movePtr(ImmMaybeNurseryPtr imm, Register dest) {
+        movePtr(noteMaybeNurseryPtr(imm), dest);
+    }
     void loadPtr(AbsoluteAddress address, Register dest) {
         if (X86Assembler::isAddressImmediate(address.addr)) {
             movq(Operand(address), dest);
         } else {
             mov(ImmPtr(address.addr), ScratchReg);
             loadPtr(Address(ScratchReg, 0x0), dest);
         }
     }
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -202,16 +202,19 @@ class Assembler : public AssemblerX86Sha
     void executableCopy(uint8_t *buffer);
 
     // Actual assembly emitting functions.
 
     void push(ImmGCPtr ptr) {
         push(Imm32(uintptr_t(ptr.value)));
         writeDataRelocation(ptr);
     }
+    void push(ImmMaybeNurseryPtr ptr) {
+        push(noteMaybeNurseryPtr(ptr));
+    }
     void push(const ImmWord imm) {
         push(Imm32(imm.value));
     }
     void push(const ImmPtr imm) {
         push(ImmWord(uintptr_t(imm.value)));
     }
     void push(FloatRegister src) {
         subl(Imm32(sizeof(double)), StackPointer);
@@ -352,16 +355,19 @@ class Assembler : public AssemblerX86Sha
           case Operand::MEM_ADDRESS32:
             masm.cmpl_im(uintptr_t(imm.value), op.address());
             writeDataRelocation(imm);
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
+    void cmpl(const Operand &op, ImmMaybeNurseryPtr imm) {
+        cmpl(op, noteMaybeNurseryPtr(imm));
+    }
     void cmpl(AsmJSAbsoluteAddress lhs, Register rhs) {
         masm.cmpl_rm_force32(rhs.code(), (void*)-1);
         append(AsmJSAbsoluteLink(CodeOffsetLabel(masm.currentOffset()), lhs.kind()));
     }
 
     void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) {
         JmpSrc src = masm.jmp();
         addPendingJump(src, target, reloc);
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -218,17 +218,17 @@ class MacroAssemblerX86 : public MacroAs
     void popValue(ValueOperand val) {
         pop(val.payloadReg());
         pop(val.typeReg());
     }
     void pushValue(const Value &val) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         push(Imm32(jv.s.tag));
         if (val.isMarkable())
-            push(ImmGCPtr(reinterpret_cast<gc::Cell *>(val.toGCThing())));
+            push(ImmMaybeNurseryPtr(reinterpret_cast<gc::Cell *>(val.toGCThing())));
         else
             push(Imm32(jv.s.payload.i32));
     }
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         push(reg);
     }
     void pushValue(const Address &addr) {
@@ -699,16 +699,19 @@ class MacroAssemblerX86 : public MacroAs
         movl(imm, dest);
     }
     void movePtr(AsmJSImmPtr imm, Register dest) {
         mov(imm, dest);
     }
     void movePtr(ImmGCPtr imm, Register dest) {
         movl(imm, dest);
     }
+    void movePtr(ImmMaybeNurseryPtr imm, Register dest) {
+        movePtr(noteMaybeNurseryPtr(imm), dest);
+    }
     void loadPtr(const Address &address, Register dest) {
         movl(Operand(address), dest);
     }
     void loadPtr(const Operand &src, Register dest) {
         movl(src, dest);
     }
     void loadPtr(const BaseIndex &src, Register dest) {
         movl(Operand(src), dest);