Bug 1437483 part 2 - Spectre mitigations for guardObjectType, disabled by default. r=nbp,luke
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 26 Feb 2018 15:26:17 +0100
changeset 457786 594ed5cfd631fe3aaad0ae37b57e0334e1023790
parent 457785 c34b095d74f1d61e52957a2772b4bf691bae26e4
child 457787 1bfa2571aade15ec4de20a37d96cfd7bb1cf3b38
push id8799
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 16:46:23 +0000
treeherdermozilla-beta@15334014dc67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp, luke
bugs1437483
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1437483 part 2 - Spectre mitigations for guardObjectType, disabled by default. r=nbp,luke
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonCacheIRCompiler.cpp
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/jit/RegisterSets.h
js/src/jit/arm/MacroAssembler-arm-inl.h
js/src/jit/arm64/MacroAssembler-arm64-inl.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/jit/shared/Lowering-shared-inl.h
js/src/jit/shared/Lowering-shared.h
js/src/jit/x64/MacroAssembler-x64-inl.h
js/src/jit/x86/MacroAssembler-x86-inl.h
js/src/vm/UnboxedObject.cpp
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3830,19 +3830,23 @@ CodeGenerator::visitLoadUnboxedExpando(L
 
 void
 CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir)
 {
     ValueOperand operand = ToValue(lir, LTypeBarrierV::Input);
     Register unboxScratch = ToTempRegisterOrInvalid(lir->unboxTemp());
     Register objScratch = ToTempRegisterOrInvalid(lir->objTemp());
 
+    // guardObjectType may zero the payload/Value register on speculative paths
+    // (we should have a defineReuseInput allocation in this case).
+    Register spectreRegToZero = operand.payloadOrValueReg();
+
     Label miss;
     masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), lir->mir()->barrierKind(),
-                      unboxScratch, objScratch, &miss);
+                      unboxScratch, objScratch, spectreRegToZero, &miss);
     bailoutFrom(&miss, lir->snapshot());
 }
 
 void
 CodeGenerator::visitTypeBarrierO(LTypeBarrierO* lir)
 {
     Register obj = ToRegister(lir->object());
     Register scratch = ToTempRegisterOrInvalid(lir->temp());
@@ -3854,36 +3858,26 @@ CodeGenerator::visitTypeBarrierO(LTypeBa
         masm.branchTestPtr(Assembler::Zero, obj, obj, nullTarget);
     } else {
         MOZ_ASSERT(lir->mir()->type() == MIRType::Object);
         MOZ_ASSERT(lir->mir()->barrierKind() != BarrierKind::TypeTagOnly);
     }
 
     if (lir->mir()->barrierKind() != BarrierKind::TypeTagOnly) {
         masm.comment("Type tag only");
-        masm.guardObjectType(obj, lir->mir()->resultTypeSet(), scratch, &miss);
+        // guardObjectType may zero the object register on speculative paths
+        // (we should have a defineReuseInput allocation in this case).
+        Register spectreRegToZero = obj;
+        masm.guardObjectType(obj, lir->mir()->resultTypeSet(), scratch, spectreRegToZero, &miss);
     }
 
     bailoutFrom(&miss, lir->snapshot());
     masm.bind(&ok);
 }
 
-void
-CodeGenerator::visitMonitorTypes(LMonitorTypes* lir)
-{
-    ValueOperand operand = ToValue(lir, LMonitorTypes::Input);
-    Register unboxScratch = ToTempRegisterOrInvalid(lir->unboxTemp());
-    Register objScratch = ToTempRegisterOrInvalid(lir->objTemp());
-
-    Label matched, miss;
-    masm.guardTypeSet(operand, lir->mir()->typeSet(), lir->mir()->barrierKind(), unboxScratch,
-                      objScratch, &miss);
-    bailoutFrom(&miss, lir->snapshot());
-}
-
 // Out-of-line path to update the store buffer.
 class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator>
 {
     LInstruction* lir_;
     const LAllocation* object_;
 
   public:
     OutOfLineCallPostWriteBarrier(LInstruction* lir, const LAllocation* object)
@@ -5086,35 +5080,47 @@ CodeGenerator::generateArgumentsChecks(b
     // arguments are correct. Upon fail it will hit a breakpoint.
 
     MIRGraph& mir = gen->graph();
     MResumePoint* rp = mir.entryResumePoint();
 
     // No registers are allocated yet, so it's safe to grab anything.
     AllocatableGeneralRegisterSet temps(GeneralRegisterSet::All());
     Register temp1 = temps.takeAny();
+#ifndef JS_CODEGEN_ARM64
     Register temp2 = temps.takeAny();
-
+#endif
     const CompileInfo& info = gen->info();
 
     Label miss;
     for (uint32_t i = info.startArgSlot(); i < info.endArgSlot(); i++) {
         // All initial parameters are guaranteed to be MParameters.
         MParameter* param = rp->getOperand(i)->toParameter();
         const TypeSet* types = param->resultTypeSet();
         if (!types || types->unknown())
             continue;
 
+#ifndef JS_CODEGEN_ARM64
         // Calculate the offset on the stack of the argument.
         // (i - info.startArgSlot())    - Compute index of arg within arg vector.
         // ... * sizeof(Value)          - Scale by value size.
         // ArgToStackOffset(...)        - Compute displacement within arg vector.
         int32_t offset = ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value));
         Address argAddr(masm.getStackPointer(), offset);
-        masm.guardTypeSet(argAddr, types, BarrierKind::TypeSet, temp1, temp2, &miss);
+
+        // guardObjectType will zero the stack pointer register on speculative
+        // paths.
+        Register spectreRegToZero = masm.getStackPointer();
+        masm.guardTypeSet(argAddr, types, BarrierKind::TypeSet, temp1, temp2,
+                          spectreRegToZero, &miss);
+#else
+        // On ARM64, the stack pointer situation is more complicated. When we
+        // enable Ion, we should figure out how to mitigate Spectre there.
+        MOZ_CRASH("NYI");
+#endif
     }
 
     if (miss.used()) {
         if (assert) {
 #ifdef DEBUG
             Label success;
             masm.jump(&success);
             masm.bind(&miss);
@@ -5404,17 +5410,17 @@ CodeGenerator::emitAssertObjectOrStringR
     if ((type == MIRType::Object || type == MIRType::ObjectOrNull) &&
         typeset && !typeset->unknownObject())
     {
         // We have a result TypeSet, assert this object is in it.
         Label miss, ok;
         if (type == MIRType::ObjectOrNull)
             masm.branchPtr(Assembler::Equal, input, ImmWord(0), &ok);
         if (typeset->getObjectCount() > 0)
-            masm.guardObjectType(input, typeset, temp, &miss);
+            masm.guardObjectType(input, typeset, temp, input, &miss);
         else
             masm.jump(&miss);
         masm.jump(&ok);
 
         masm.bind(&miss);
         masm.guardTypeSetMightBeIncomplete(typeset, input, temp, &ok);
 
         masm.assumeUnreachable("MIR instruction returned object with unexpected type");
@@ -5470,17 +5476,18 @@ CodeGenerator::emitAssertResultV(const V
     // Don't check if the script has been invalidated. In that case invalid
     // types are expected (until we reach the OsiPoint and bailout).
     Label done;
     branchIfInvalidated(temp1, &done);
 
     if (typeset && !typeset->unknown()) {
         // We have a result TypeSet, assert this value is in it.
         Label miss, ok;
-        masm.guardTypeSet(input, typeset, BarrierKind::TypeSet, temp1, temp2, &miss);
+        masm.guardTypeSet(input, typeset, BarrierKind::TypeSet, temp1, temp2,
+                          input.payloadOrValueReg(), &miss);
         masm.jump(&ok);
 
         masm.bind(&miss);
 
         // Check for cases where the type set guard might have missed due to
         // changing object groups.
         Label realMiss;
         masm.branchTestObject(Assembler::NotEqual, input, &realMiss);
@@ -5516,18 +5523,20 @@ CodeGenerator::emitAssertResultV(const V
 
 void
 CodeGenerator::emitObjectOrStringResultChecks(LInstruction* lir, MDefinition* mir)
 {
     if (lir->numDefs() == 0)
         return;
 
     MOZ_ASSERT(lir->numDefs() == 1);
+    if (lir->getDef(0)->isBogusTemp())
+        return;
+
     Register output = ToRegister(lir->getDef(0));
-
     emitAssertObjectOrStringResult(output, mir->type(), mir->resultTypeSet());
 }
 
 void
 CodeGenerator::emitValueResultChecks(LInstruction* lir, MDefinition* mir)
 {
     if (lir->numDefs() == 0)
         return;
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -156,17 +156,16 @@ class CodeGenerator final : public CodeG
     void visitMaybeToDoubleElement(LMaybeToDoubleElement* lir);
     void visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir);
     void visitGuardObjectIdentity(LGuardObjectIdentity* guard);
     void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir);
     void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir);
     void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir);
     void visitTypeBarrierV(LTypeBarrierV* lir);
     void visitTypeBarrierO(LTypeBarrierO* lir);
-    void visitMonitorTypes(LMonitorTypes* lir);
     void emitPostWriteBarrier(const LAllocation* obj);
     void emitPostWriteBarrier(Register objreg);
     void emitPostWriteBarrierS(Address address, Register prev, Register next);
     template <class LPostBarrierType, MIRType nurseryType>
     void visitPostWriteBarrierCommon(LPostBarrierType* lir, OutOfLineCode* ool);
     template <class LPostBarrierType>
     void visitPostWriteBarrierCommonV(LPostBarrierType* lir, OutOfLineCode* ool);
     void visitPostWriteBarrierO(LPostWriteBarrierO* lir);
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -1434,26 +1434,32 @@ EmitCheckPropertyTypes(MacroAssembler& m
     HeapTypeSet* propTypes = group->maybeGetProperty(id);
     if (propTypes && propTypes->unknown())
         return;
 
     // Use the object register as scratch, as we don't need it here.
     masm.Push(obj);
     Register scratch1 = obj;
 
-    // We may also need a scratch register for guardTypeSet.
+    // We may also need a scratch register for guardTypeSet. Additionally,
+    // spectreRegToZero is the register that may be zeroed on speculatively
+    // executed paths.
     Register objScratch = InvalidReg;
+    Register spectreRegToZero = InvalidReg;
     if (propTypes && !propTypes->unknownObject() && propTypes->getObjectCount() > 0) {
         AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
         if (!val.constant()) {
             TypedOrValueRegister valReg = val.reg();
-            if (valReg.hasValue())
+            if (valReg.hasValue()) {
                 regs.take(valReg.valueReg());
-            else if (!valReg.typedReg().isFloat())
+                spectreRegToZero = valReg.valueReg().payloadOrValueReg();
+            } else if (!valReg.typedReg().isFloat()) {
                 regs.take(valReg.typedReg().gpr());
+                spectreRegToZero = valReg.typedReg().gpr();
+            }
         }
         regs.take(scratch1);
         objScratch = regs.takeAny();
         masm.Push(objScratch);
     }
 
     bool checkTypeSet = true;
     Label failedFastPath;
@@ -1480,17 +1486,17 @@ EmitCheckPropertyTypes(MacroAssembler& m
 
     Label done;
     if (checkTypeSet) {
         TypedOrValueRegister valReg = val.reg();
         if (propTypes) {
             // guardTypeSet can read from type sets without triggering read barriers.
             TypeSet::readBarrier(propTypes);
             masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratch1, objScratch,
-                              &failedFastPath);
+                              spectreRegToZero, &failedFastPath);
             masm.jump(&done);
         } else {
             masm.jump(&failedFastPath);
         }
     }
 
     if (failedFastPath.used()) {
         // The inline type check failed. Do a callWithABI to check the current
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2801,69 +2801,68 @@ LIRGenerator::visitTypeBarrier(MTypeBarr
     if (ins->alwaysBails()) {
         LBail* bail = new(alloc()) LBail();
         assignSnapshot(bail, Bailout_Inevitable);
         add(bail, ins);
         redefine(ins, ins->input());
         return;
     }
 
-    bool needObjTemp = !types->unknownObject() && types->getObjectCount() > 0;
+    bool hasSpecificObjects = !types->unknownObject() && types->getObjectCount() > 0;
 
     // Handle typebarrier with Value as input.
     if (inputType == MIRType::Value) {
-        LDefinition objTemp = needObjTemp ? temp() : LDefinition::BogusTemp();
-        LTypeBarrierV* barrier = new(alloc()) LTypeBarrierV(useBox(ins->input()), tempToUnbox(),
-                                                            objTemp);
-        assignSnapshot(barrier, Bailout_TypeBarrierV);
-        add(barrier, ins);
-        redefine(ins, ins->input());
+        LDefinition objTemp = hasSpecificObjects ? temp() : LDefinition::BogusTemp();
+        if (ins->canRedefineInput()) {
+            LTypeBarrierV* barrier =
+                new(alloc()) LTypeBarrierV(useBox(ins->input()), tempToUnbox(), objTemp);
+            assignSnapshot(barrier, Bailout_TypeBarrierV);
+            add(barrier, ins);
+            redefine(ins, ins->input());
+        } else {
+            LTypeBarrierV* barrier =
+                new(alloc()) LTypeBarrierV(useBoxAtStart(ins->input()), tempToUnbox(), objTemp);
+            assignSnapshot(barrier, Bailout_TypeBarrierV);
+            defineBoxReuseInput(barrier, ins, 0);
+        }
         return;
     }
 
     // The payload needs to be tested if it either might be null or might have
     // an object that should be excluded from the barrier.
     bool needsObjectBarrier = false;
     if (inputType == MIRType::ObjectOrNull)
         needsObjectBarrier = true;
     if (inputType == MIRType::Object && !types->hasType(TypeSet::AnyObjectType()) &&
         ins->barrierKind() != BarrierKind::TypeTagOnly)
     {
         needsObjectBarrier = true;
     }
 
     if (needsObjectBarrier) {
-        LDefinition tmp = needObjTemp ? temp() : LDefinition::BogusTemp();
-        LTypeBarrierO* barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp);
-        assignSnapshot(barrier, Bailout_TypeBarrierO);
-        add(barrier, ins);
-        redefine(ins, ins->getOperand(0));
+        LDefinition tmp = hasSpecificObjects ? temp() : LDefinition::BogusTemp();
+        if (ins->canRedefineInput()) {
+            LTypeBarrierO* barrier =
+                new(alloc()) LTypeBarrierO(useRegister(ins->input()), tmp);
+            assignSnapshot(barrier, Bailout_TypeBarrierO);
+            add(barrier, ins);
+            redefine(ins, ins->getOperand(0));
+        } else {
+            LTypeBarrierO* barrier =
+                new(alloc()) LTypeBarrierO(useRegisterAtStart(ins->input()), tmp);
+            assignSnapshot(barrier, Bailout_TypeBarrierO);
+            defineReuseInput(barrier, ins, 0);
+        }
         return;
     }
 
     // Handle remaining cases: No-op, unbox did everything.
     redefine(ins, ins->getOperand(0));
 }
 
-void
-LIRGenerator::visitMonitorTypes(MMonitorTypes* ins)
-{
-    // Requesting a non-GC pointer is safe here since we never re-enter C++
-    // from inside a type check.
-
-    const TemporaryTypeSet* types = ins->typeSet();
-
-    bool needObjTemp = !types->unknownObject() && types->getObjectCount() > 0;
-    LDefinition objTemp = needObjTemp ? temp() : LDefinition::BogusTemp();
-
-    LMonitorTypes* lir = new(alloc()) LMonitorTypes(useBox(ins->input()), tempToUnbox(), objTemp);
-    assignSnapshot(lir, Bailout_MonitorTypes);
-    add(lir, ins);
-}
-
 // Returns true iff |def| is a constant that's either not a GC thing or is not
 // allocated in the nursery.
 static bool
 IsNonNurseryConstant(MDefinition* def)
 {
     if (!def->isConstant())
         return false;
     Value v = def->toConstant()->toJSValue();
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -211,17 +211,16 @@ class LIRGenerator : public LIRGenerator
     void visitHomeObject(MHomeObject* ins) override;
     void visitHomeObjectSuperBase(MHomeObjectSuperBase* ins) override;
     void visitInterruptCheck(MInterruptCheck* ins) override;
     void visitWasmTrap(MWasmTrap* ins) override;
     void visitWasmReinterpret(MWasmReinterpret* ins) override;
     void visitStoreSlot(MStoreSlot* ins) override;
     void visitFilterTypeSet(MFilterTypeSet* ins) override;
     void visitTypeBarrier(MTypeBarrier* ins) override;
-    void visitMonitorTypes(MMonitorTypes* ins) override;
     void visitPostWriteBarrier(MPostWriteBarrier* ins) override;
     void visitPostWriteElementBarrier(MPostWriteElementBarrier* ins) override;
     void visitArrayLength(MArrayLength* ins) override;
     void visitSetArrayLength(MSetArrayLength* ins) override;
     void visitGetNextEntryForIterator(MGetNextEntryForIterator* ins) override;
     void visitTypedArrayLength(MTypedArrayLength* ins) override;
     void visitTypedArrayElements(MTypedArrayElements* ins) override;
     void visitSetDisjointTypedElements(MSetDisjointTypedElements* ins) override;
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2356,16 +2356,39 @@ MTypeBarrier::foldsTo(TempAllocator& all
         return this;
 
     if (input()->type() != type)
         return this;
 
     return input();
 }
 
+bool
+MTypeBarrier::canRedefineInput()
+{
+    // LTypeBarrier does not need its own def usually, because we can use the
+    // input's allocation (LIRGenerator::redefineInput). However, if Spectre
+    // mitigations are enabled, guardObjectType may zero the object register on
+    // speculatively executed paths, so LTypeBarrier needs to have its own def
+    // then to guarantee all uses will see this potentially-zeroed value.
+
+    if (!JitOptions.spectreObjectMitigationsBarriers)
+        return true;
+
+    if (barrierKind() == BarrierKind::TypeTagOnly)
+        return true;
+
+    TemporaryTypeSet* types = resultTypeSet();
+    bool hasSpecificObjects = !types->unknownObject() && types->getObjectCount() > 0;
+    if (!hasSpecificObjects)
+        return true;
+
+    return false;
+}
+
 #ifdef DEBUG
 void
 MPhi::assertLoopPhi() const
 {
     // getLoopPredecessorOperand and getLoopBackedgeOperand rely on these
     // predecessors being at indices 0 and 1.
     MBasicBlock* pred = block()->getPredecessor(0);
     MBasicBlock* back = block()->getPredecessor(1);
@@ -6631,18 +6654,27 @@ TryAddTypeBarrierForWrite(TempAllocator&
         return false;
 
     // If all possible objects can be stored without a barrier, we don't have to
     // guard on the specific object types.
     BarrierKind kind = BarrierKind::TypeSet;
     if ((*pvalue)->resultTypeSet() && (*pvalue)->resultTypeSet()->objectsAreSubset(types))
         kind = BarrierKind::TypeTagOnly;
 
-    MInstruction* ins = MMonitorTypes::New(alloc, *pvalue, types, kind);
+    MInstruction* ins = MTypeBarrier::New(alloc, *pvalue, types, kind);
     current->add(ins);
+    ins->setNotMovable();
+    if (ins->type() == MIRType::Undefined) {
+        ins = MConstant::New(alloc, UndefinedValue());
+        current->add(ins);
+    } else if (ins->type() == MIRType::Null) {
+        ins = MConstant::New(alloc, NullValue());
+        current->add(ins);
+    }
+    *pvalue = ins;
     return true;
 }
 
 static MInstruction*
 AddGroupGuard(TempAllocator& alloc, MBasicBlock* current, MDefinition* obj,
               TypeSet::ObjectKey* key, bool bailOnEquality)
 {
     MInstruction* guard;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13218,16 +13218,18 @@ class MTypeBarrier
     virtual bool neverHoist() const override {
         return resultTypeSet()->empty();
     }
     BarrierKind barrierKind() const {
         return barrierKind_;
     }
     MDefinition* foldsTo(TempAllocator& alloc) override;
 
+    bool canRedefineInput();
+
     bool alwaysBails() const {
         // If mirtype of input doesn't agree with mirtype of barrier,
         // we will definitely bail.
         MIRType type = resultTypeSet()->getKnownMIRType();
         if (type == MIRType::Value)
             return false;
         if (input()->type() == MIRType::Value)
             return false;
@@ -13238,53 +13240,16 @@ class MTypeBarrier
             return false;
         }
         return input()->type() != type;
     }
 
     ALLOW_CLONE(MTypeBarrier)
 };
 
-// Like MTypeBarrier, guard that the value is in the given type set. This is
-// used before property writes to ensure the value being written is represented
-// in the property types for the object.
-class MMonitorTypes
-  : public MUnaryInstruction,
-    public BoxInputsPolicy::Data
-{
-    const TemporaryTypeSet* typeSet_;
-    BarrierKind barrierKind_;
-
-    MMonitorTypes(MDefinition* def, const TemporaryTypeSet* types, BarrierKind kind)
-      : MUnaryInstruction(classOpcode, def),
-        typeSet_(types),
-        barrierKind_(kind)
-    {
-        MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
-
-        setGuard();
-        MOZ_ASSERT(!types->unknown());
-    }
-
-  public:
-    INSTRUCTION_HEADER(MonitorTypes)
-    TRIVIAL_NEW_WRAPPERS
-
-    const TemporaryTypeSet* typeSet() const {
-        return typeSet_;
-    }
-    BarrierKind barrierKind() const {
-        return barrierKind_;
-    }
-
-    AliasSet getAliasSet() const override {
-        return AliasSet::None();
-    }
-};
-
 // Given a value being written to another object, update the generational store
 // buffer if the value is in the nursery and object is in the tenured heap.
 class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0>::Data
 {
     MPostWriteBarrier(MDefinition* obj, MDefinition* value)
       : MBinaryInstruction(classOpcode, obj, value)
     {
         setGuard();
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -181,17 +181,16 @@ namespace jit {
     _(StoreSlot)                                                            \
     _(FunctionEnvironment)                                                  \
     _(NewLexicalEnvironmentObject)                                          \
     _(CopyLexicalEnvironmentObject)                                         \
     _(HomeObject)                                                           \
     _(HomeObjectSuperBase)                                                  \
     _(FilterTypeSet)                                                        \
     _(TypeBarrier)                                                          \
-    _(MonitorTypes)                                                         \
     _(PostWriteBarrier)                                                     \
     _(PostWriteElementBarrier)                                              \
     _(GetPropSuperCache)                                                    \
     _(GetPropertyCache)                                                     \
     _(GetPropertyPolymorphic)                                               \
     _(SetPropertyPolymorphic)                                               \
     _(BindNameCache)                                                        \
     _(CallBindVar)                                                          \
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -75,23 +75,27 @@ EmitTypeCheck(MacroAssembler& masm, Asse
         break;
       default:
         MOZ_CRASH("Unexpected type");
     }
 }
 
 template <typename Source> void
 MacroAssembler::guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind,
-                             Register unboxScratch, Register objScratch, Label* miss)
+                             Register unboxScratch, Register objScratch,
+                             Register spectreRegToZero, Label* miss)
 {
     // unboxScratch may be InvalidReg on 32-bit platforms. It should only be
     // used for extracting the Value tag or payload.
     //
     // objScratch may be InvalidReg if the TypeSet does not contain specific
     // objects to guard on. It should only be used for guardObjectType.
+    //
+    // spectreRegToZero is a register that will be zeroed by guardObjectType on
+    // speculatively executed paths.
 
     MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
     MOZ_ASSERT(!types->unknown());
 
     Label matched;
     TypeSet::Type tests[8] = {
         TypeSet::Int32Type(),
         TypeSet::UndefinedType(),
@@ -149,22 +153,22 @@ MacroAssembler::guardTypeSet(const Sourc
     MOZ_ASSERT(objScratch != InvalidReg);
     MOZ_ASSERT(objScratch != unboxScratch);
 
     MOZ_ASSERT(numBranches == 1);
     branchTestObject(NotEqual, tag, miss);
 
     if (kind != BarrierKind::TypeTagOnly) {
         Register obj = extractObject(address, unboxScratch);
-        guardObjectType(obj, types, objScratch, miss);
+        guardObjectType(obj, types, objScratch, spectreRegToZero, miss);
     } else {
 #ifdef DEBUG
         Label fail;
         Register obj = extractObject(address, unboxScratch);
-        guardObjectType(obj, types, objScratch, &fail);
+        guardObjectType(obj, types, objScratch, spectreRegToZero, &fail);
         jump(&matched);
 
         bind(&fail);
         guardTypeSetMightBeIncomplete(types, obj, objScratch, &matched);
         assumeUnreachable("Unexpected object type");
 #endif
     }
 
@@ -205,18 +209,18 @@ MacroAssembler::guardTypeSetMightBeIncom
         }
         branchTest32(Assembler::NonZero, Address(scratch, ObjectGroup::offsetOfFlags()),
                      Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), label);
     }
 }
 #endif
 
 void
-MacroAssembler::guardObjectType(Register obj, const TypeSet* types,
-                                Register scratch, Label* miss)
+MacroAssembler::guardObjectType(Register obj, const TypeSet* types, Register scratch,
+                                Register spectreRegToZero, Label* miss)
 {
     MOZ_ASSERT(obj != scratch);
     MOZ_ASSERT(!types->unknown());
     MOZ_ASSERT(!types->hasType(TypeSet::AnyObjectType()));
     MOZ_ASSERT_IF(types->getObjectCount() > 0, scratch != InvalidReg);
 
     // Note: this method elides read barriers on values read from type sets, as
     // this may be called off thread during Ion compilation. This is
@@ -241,60 +245,96 @@ MacroAssembler::guardObjectType(Register
         }
     }
 
     if (numBranches == 0) {
         jump(miss);
         return;
     }
 
+    if (JitOptions.spectreObjectMitigationsBarriers)
+        move32(Imm32(0), scratch);
+
     if (hasSingletons) {
         for (unsigned i = 0; i < count; i++) {
             JSObject* singleton = types->getSingletonNoBarrier(i);
             if (!singleton)
                 continue;
 
-            if (--numBranches > 0)
-                branchPtr(Equal, obj, ImmGCPtr(singleton), &matched);
-            else
-                branchPtr(NotEqual, obj, ImmGCPtr(singleton), miss);
+            if (JitOptions.spectreObjectMitigationsBarriers) {
+                if (--numBranches > 0) {
+                    Label next;
+                    branchPtr(NotEqual, obj, ImmGCPtr(singleton), &next);
+                    spectreMovePtr(NotEqual, scratch, spectreRegToZero);
+                    jump(&matched);
+                    bind(&next);
+                } else {
+                    branchPtr(NotEqual, obj, ImmGCPtr(singleton), miss);
+                    spectreMovePtr(NotEqual, scratch, spectreRegToZero);
+                }
+            } else {
+                if (--numBranches > 0)
+                    branchPtr(Equal, obj, ImmGCPtr(singleton), &matched);
+                else
+                    branchPtr(NotEqual, obj, ImmGCPtr(singleton), miss);
+            }
         }
     }
 
     if (hasObjectGroups) {
         comment("has object groups");
 
-        loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
+        // If Spectre mitigations are enabled, we use the scratch register as
+        // zero register. Without mitigations we can use it to store the group.
+        Address groupAddr(obj, JSObject::offsetOfGroup());
+        if (!JitOptions.spectreObjectMitigationsBarriers)
+            loadPtr(groupAddr, scratch);
 
         for (unsigned i = 0; i < count; i++) {
             ObjectGroup* group = types->getGroupNoBarrier(i);
             if (!group)
                 continue;
 
-            if (--numBranches > 0)
-                branchPtr(Equal, scratch, ImmGCPtr(group), &matched);
-            else
-                branchPtr(NotEqual, scratch, ImmGCPtr(group), miss);
+            if (JitOptions.spectreObjectMitigationsBarriers) {
+                if (--numBranches > 0) {
+                    Label next;
+                    branchPtr(NotEqual, groupAddr, ImmGCPtr(group), &next);
+                    spectreMovePtr(NotEqual, scratch, spectreRegToZero);
+                    jump(&matched);
+                    bind(&next);
+                } else {
+                    branchPtr(NotEqual, groupAddr, ImmGCPtr(group), miss);
+                    spectreMovePtr(NotEqual, scratch, spectreRegToZero);
+                }
+            } else {
+                if (--numBranches > 0)
+                    branchPtr(Equal, scratch, ImmGCPtr(group), &matched);
+                else
+                    branchPtr(NotEqual, scratch, ImmGCPtr(group), miss);
+            }
         }
     }
 
     MOZ_ASSERT(numBranches == 0);
 
     bind(&matched);
 }
 
 template void MacroAssembler::guardTypeSet(const Address& address, const TypeSet* types,
                                            BarrierKind kind, Register unboxScratch,
-                                           Register objScratch, Label* miss);
+                                           Register objScratch, Register spectreRegToZero,
+                                           Label* miss);
 template void MacroAssembler::guardTypeSet(const ValueOperand& value, const TypeSet* types,
                                            BarrierKind kind, Register unboxScratch,
-                                           Register objScratch, Label* miss);
+                                           Register objScratch, Register spectreRegToZero,
+                                           Label* miss);
 template void MacroAssembler::guardTypeSet(const TypedOrValueRegister& value, const TypeSet* types,
                                            BarrierKind kind, Register unboxScratch,
-                                           Register objScratch, Label* miss);
+                                           Register objScratch, Register spectreRegToZero,
+                                           Label* miss);
 
 template<typename S, typename T>
 static void
 StoreToTypedFloatArray(MacroAssembler& masm, int arrayType, const S& value, const T& dest,
                        unsigned numElems)
 {
     switch (arrayType) {
       case Scalar::Float32:
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1362,16 +1362,20 @@ class MacroAssembler : public MacroAssem
     inline void test32LoadPtr(Condition cond, const Address& addr, Imm32 mask, const Address& src,
                               Register dest)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
     inline void test32MovePtr(Condition cond, const Address& addr, Imm32 mask, Register src,
                               Register dest)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
+    // Conditional move for Spectre mitigations.
+    inline void spectreMovePtr(Condition cond, Register src, Register dest)
+        DEFINED_ON(arm, arm64, x86, x64);
+
     // Performs a bounds check and zeroes the index register if out-of-bounds
     // (to mitigate Spectre).
     inline void boundsCheck32ForLoad(Register index, Register length, Register scratch,
                                      Label* failure)
         DEFINED_ON(arm, arm64, mips_shared, x86_shared);
     inline void boundsCheck32ForLoad(Register index, const Address& length, Register scratch,
                                      Label* failure)
         DEFINED_ON(arm, arm64, mips_shared, x86_shared);
@@ -1929,19 +1933,21 @@ class MacroAssembler : public MacroAssem
 
     //}}} check_macroassembler_decl_style
   public:
 
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source>
     void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind,
-                      Register unboxScratch, Register objScratch, Label* miss);
-
-    void guardObjectType(Register obj, const TypeSet* types, Register scratch, Label* miss);
+                      Register unboxScratch, Register objScratch, Register spectreRegToZero,
+                      Label* miss);
+
+    void guardObjectType(Register obj, const TypeSet* types, Register scratch,
+                         Register spectreRegToZero, Label* miss);
 
 #ifdef DEBUG
     void guardTypeSetMightBeIncomplete(const TypeSet* types, Register obj, Register scratch,
                                        Label* label);
 #endif
 
     void loadObjShape(Register objReg, Register dest) {
         loadPtr(Address(objReg, ShapedObject::offsetOfShape()), dest);
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -125,17 +125,17 @@ class ValueOperand
         return type_;
     }
     Register payloadReg() const {
         return payload_;
     }
     bool aliases(Register reg) const {
         return type_ == reg || payload_ == reg;
     }
-    Register scratchReg() const {
+    Register payloadOrValueReg() const {
         return payloadReg();
     }
     constexpr bool operator==(const ValueOperand& o) const {
         return type_ == o.type_ && payload_ == o.payload_;
     }
     constexpr bool operator!=(const ValueOperand& o) const {
         return !(*this == o);
     }
@@ -149,27 +149,31 @@ class ValueOperand
     { }
 
     Register valueReg() const {
         return value_;
     }
     bool aliases(Register reg) const {
         return value_ == reg;
     }
-    Register scratchReg() const {
+    Register payloadOrValueReg() const {
         return valueReg();
     }
     constexpr bool operator==(const ValueOperand& o) const {
         return value_ == o.value_;
     }
     constexpr bool operator!=(const ValueOperand& o) const {
         return !(*this == o);
     }
 #endif
 
+    Register scratchReg() const {
+        return payloadOrValueReg();
+    }
+
     ValueOperand() = default;
 };
 
 // Registers to hold either either a typed or untyped value.
 class TypedOrValueRegister
 {
     // Type of value being stored.
     MIRType type_;
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -2177,16 +2177,22 @@ MacroAssembler::test32MovePtr(Condition 
                               Register dest)
 {
     MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
     test32(addr, mask);
     ma_mov(src, dest, LeaveCC, cond);
 }
 
 void
+MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest)
+{
+    ma_mov(src, dest, LeaveCC, cond);
+}
+
+void
 MacroAssembler::boundsCheck32ForLoad(Register index, Register length, Register scratch,
                                      Label* failure)
 {
     MOZ_ASSERT(index != length);
     MOZ_ASSERT(length != scratch);
     MOZ_ASSERT(index != scratch);
 
     if (JitOptions.spectreIndexMasking)
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -1756,16 +1756,22 @@ MacroAssembler::test32MovePtr(Condition 
                               Register dest)
 {
     MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
     test32(addr, mask);
     Csel(ARMRegister(dest, 64), ARMRegister(src, 64), ARMRegister(dest, 64), cond);
 }
 
 void
+MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest)
+{
+    Csel(ARMRegister(dest, 64), ARMRegister(src, 64), ARMRegister(dest, 64), cond);
+}
+
+void
 MacroAssembler::boundsCheck32ForLoad(Register index, Register length, Register scratch,
                                      Label* failure)
 {
     MOZ_ASSERT(index != length);
     MOZ_ASSERT(length != scratch);
     MOZ_ASSERT(index != scratch);
 
     branch32(Assembler::BelowOrEqual, length, index, failure);
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -7783,17 +7783,17 @@ class LLoadUnboxedExpando : public LInst
         return getOperand(0);
     }
     const MLoadUnboxedExpando* mir() const {
         return mir_->toLoadUnboxedExpando();
     }
 };
 
 // Guard that a value is in a TypeSet.
-class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 2>
+class LTypeBarrierV : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 2>
 {
   public:
     LIR_HEADER(TypeBarrierV)
 
     LTypeBarrierV(const LBoxAllocation& input, const LDefinition& unboxTemp,
                   const LDefinition& objTemp) {
         setBoxOperand(Input, input);
         setTemp(0, unboxTemp);
@@ -7809,17 +7809,17 @@ class LTypeBarrierV : public LInstructio
         return getTemp(0);
     }
     const LDefinition* objTemp() {
         return getTemp(1);
     }
 };
 
 // Guard that a object is in a TypeSet.
-class LTypeBarrierO : public LInstructionHelper<0, 1, 1>
+class LTypeBarrierO : public LInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(TypeBarrierO)
 
     LTypeBarrierO(const LAllocation& obj, const LDefinition& temp) {
         setOperand(0, obj);
         setTemp(0, temp);
     }
@@ -7829,42 +7829,16 @@ class LTypeBarrierO : public LInstructio
     const LAllocation* object() {
         return getOperand(0);
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
-// Guard that a value is in a TypeSet.
-class LMonitorTypes : public LInstructionHelper<0, BOX_PIECES, 2>
-{
-  public:
-    LIR_HEADER(MonitorTypes)
-
-    LMonitorTypes(const LBoxAllocation& input, const LDefinition& unboxTemp,
-                  const LDefinition& objTemp) {
-        setBoxOperand(Input, input);
-        setTemp(0, unboxTemp);
-        setTemp(1, objTemp);
-    }
-
-    static const size_t Input = 0;
-
-    const MMonitorTypes* mir() const {
-        return mir_->toMonitorTypes();
-    }
-    const LDefinition* unboxTemp() {
-        return getTemp(0);
-    }
-    const LDefinition* objTemp() {
-        return getTemp(1);
-    }
-};
-
 // Generational write barrier used when writing an object to another object.
 class LPostWriteBarrierO : public LInstructionHelper<0, 2, 1>
 {
   public:
     LIR_HEADER(PostWriteBarrierO)
 
     LPostWriteBarrierO(const LAllocation& obj, const LAllocation& value,
                        const LDefinition& temp) {
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -265,17 +265,16 @@
     _(GuardReceiverPolymorphic)     \
     _(GuardObjectGroup)             \
     _(GuardObjectIdentity)          \
     _(GuardClass)                   \
     _(GuardUnboxedExpando)          \
     _(LoadUnboxedExpando)           \
     _(TypeBarrierV)                 \
     _(TypeBarrierO)                 \
-    _(MonitorTypes)                 \
     _(PostWriteBarrierO)            \
     _(PostWriteBarrierS)            \
     _(PostWriteBarrierV)            \
     _(PostWriteElementBarrierO)     \
     _(PostWriteElementBarrierS)     \
     _(PostWriteElementBarrierV)     \
     _(InitializedLength)            \
     _(SetInitializedLength)         \
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -154,16 +154,56 @@ LIRGeneratorShared::defineInt64ReuseInpu
 #endif
 
     lir->setMir(mir);
     mir->setVirtualRegister(vreg);
     add(lir);
 
 }
 
+template <size_t Ops, size_t Temps> void
+LIRGeneratorShared::defineBoxReuseInput(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir,
+                                        MDefinition* mir, uint32_t operand)
+{
+    // The input should be used at the start of the instruction, to avoid moves.
+    MOZ_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart());
+#ifdef JS_NUNBOX32
+    MOZ_ASSERT(lir->getOperand(operand + 1)->toUse()->usedAtStart());
+#endif
+    MOZ_ASSERT(!lir->isCall());
+    MOZ_ASSERT(mir->type() == MIRType::Value);
+
+    uint32_t vreg = getVirtualRegister();
+
+#ifdef JS_NUNBOX32
+    static_assert(VREG_TYPE_OFFSET == 0, "Code below assumes VREG_TYPE_OFFSET == 0");
+    static_assert(VREG_DATA_OFFSET == 1, "Code below assumes VREG_DATA_OFFSET == 1");
+
+    LDefinition def1(LDefinition::TYPE, LDefinition::MUST_REUSE_INPUT);
+    def1.setReusedInput(operand);
+    def1.setVirtualRegister(vreg);
+    lir->setDef(0, def1);
+
+    getVirtualRegister();
+    LDefinition def2(LDefinition::PAYLOAD, LDefinition::MUST_REUSE_INPUT);
+    def2.setReusedInput(operand + 1);
+    def2.setVirtualRegister(vreg + 1);
+    lir->setDef(1, def2);
+#else
+    LDefinition def(LDefinition::BOX, LDefinition::MUST_REUSE_INPUT);
+    def.setReusedInput(operand);
+    def.setVirtualRegister(vreg);
+    lir->setDef(0, def);
+#endif
+
+    lir->setMir(mir);
+    mir->setVirtualRegister(vreg);
+    add(lir);
+}
+
 template <size_t Temps> void
 LIRGeneratorShared::defineBox(details::LInstructionFixedDefsTempsHelper<BOX_PIECES, Temps>* lir,
                               MDefinition* mir, LDefinition::Policy policy)
 {
     // Call instructions should use defineReturn.
     MOZ_ASSERT(!lir->isCall());
     MOZ_ASSERT(mir->type() == MIRType::Value);
 
@@ -674,18 +714,18 @@ LIRGeneratorShared::add(T* ins, MInstruc
 static inline uint32_t
 VirtualRegisterOfPayload(MDefinition* mir)
 {
     if (mir->isBox()) {
         MDefinition* inner = mir->toBox()->getOperand(0);
         if (!inner->isConstant() && inner->type() != MIRType::Double && inner->type() != MIRType::Float32)
             return inner->virtualRegister();
     }
-    if (mir->isTypeBarrier())
-        return VirtualRegisterOfPayload(mir->getOperand(0));
+    if (mir->isTypeBarrier() && mir->toTypeBarrier()->canRedefineInput())
+        return VirtualRegisterOfPayload(mir->toTypeBarrier()->input());
     return mir->virtualRegister() + VREG_DATA_OFFSET;
 }
 
 // Note: always call ensureDefined before calling useType/usePayload,
 // so that emitted-at-use operands are handled correctly.
 LUse
 LIRGeneratorShared::useType(MDefinition* mir, LUse::Policy policy)
 {
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -183,16 +183,20 @@ class LIRGeneratorShared : public MDefin
     inline void define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
                        const LDefinition& def);
 
     template <size_t Ops, size_t Temps>
     inline void defineReuseInput(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir,
                                  uint32_t operand);
 
     template <size_t Ops, size_t Temps>
+    inline void defineBoxReuseInput(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir,
+                                    MDefinition* mir, uint32_t operand);
+
+    template <size_t Ops, size_t Temps>
     inline void defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir,
                                       MDefinition* mir, uint32_t operand);
 
     // Returns a box allocation for a Value-typed instruction.
     inline LBoxAllocation useBox(MDefinition* mir, LUse::Policy policy = LUse::REGISTER,
                                  bool useAtStart = false);
 
     // Returns a box allocation. The use is either typed, a Value, or
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -841,16 +841,22 @@ void
 MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, Register src,
                               Register dest)
 {
     MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
     test32(addr, mask);
     cmovCCq(cond, Operand(src), dest);
 }
 
+void
+MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest)
+{
+    cmovCCq(cond, Operand(src), dest);
+}
+
 // ========================================================================
 // Truncate floating point.
 
 void
 MacroAssembler::truncateFloat32ToUInt64(Address src, Address dest, Register temp,
                                         FloatRegister floatTemp)
 {
     Label done;
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -1030,16 +1030,22 @@ void
 MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, Register src,
                               Register dest)
 {
     MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
     test32(addr, mask);
     cmovCCl(cond, Operand(src), dest);
 }
 
+void
+MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest)
+{
+    cmovCCl(cond, Operand(src), dest);
+}
+
 // ========================================================================
 // Truncate floating point.
 
 void
 MacroAssembler::truncateFloat32ToUInt64(Address src, Address dest, Register temp,
                                         FloatRegister floatTemp)
 {
     Label done;
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -196,17 +196,17 @@ UnboxedLayout::makeConstructorCode(JSCon
             Label notObject;
             masm.branchTestObject(Assembler::NotEqual, valueOperand,
                                   types->mightBeMIRType(MIRType::Null) ? &notObject : &failureStoreObject);
 
             Register payloadReg = masm.extractObject(valueOperand, scratch1);
 
             if (!types->hasType(TypeSet::AnyObjectType())) {
                 Register scratch = (payloadReg == scratch1) ? scratch2 : scratch1;
-                masm.guardObjectType(payloadReg, types, scratch, &failureStoreObject);
+                masm.guardObjectType(payloadReg, types, scratch, payloadReg, &failureStoreObject);
             }
 
             masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT,
                                       TypedOrValueRegister(MIRType::Object,
                                                            AnyRegister(payloadReg)), nullptr);
 
             if (notObject.used()) {
                 Label done;