Bug 1139152 - IonMonkey: Add dynamic output type checks for LIRs that use redefine, r=jandem
authorHannes Verschore <hv1989@gmail.com>
Wed, 18 Mar 2015 10:08:39 +0100
changeset 234228 cdf93416b39aa31381ec02733954068b234c1672
parent 234227 ad0e48f5588c407d65638e183e4e45b8cdc22316
child 234229 f82a7f0db599cb3bb6e86c30f2c3f5dda34ccd1b
push id28439
push userkwierso@gmail.com
push dateWed, 18 Mar 2015 22:01:25 +0000
treeherdermozilla-central@e3bf27190360 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1139152
milestone39.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 1139152 - IonMonkey: Add dynamic output type checks for LIRs that use redefine, r=jandem
js/src/jit-test/tests/ion/bug1139152.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/JitOptions.cpp
js/src/jit/JitOptions.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jit/arm/Lowering-arm.cpp
js/src/jit/arm/Lowering-arm.h
js/src/jit/mips/Lowering-mips.cpp
js/src/jit/mips/Lowering-mips.h
js/src/jit/none/Lowering-none.h
js/src/jit/shared/Lowering-shared-inl.h
js/src/jit/shared/Lowering-shared.h
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x64/Lowering-x64.h
js/src/jit/x86/Lowering-x86.cpp
js/src/jit/x86/Lowering-x86.h
js/src/shell/js.cpp
js/src/tests/lib/tests.py
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1139152.js
@@ -0,0 +1,25 @@
+function toLiteralSource(value) {
+    if (value === null) {
+        return 'null';
+    }
+    if (typeof value === 'string') {
+        return escapeString(value);
+    }
+    if (typeof value === 'number') {
+        return generateNumber(value);
+    }
+    if (typeof value === 'boolean') {
+        return value ? 'true' : 'false';
+    }
+    value.test();
+}
+
+function test(x) {
+   var b = x ? true : {};
+   return toLiteralSource(b);
+}
+
+var output = true
+for (var i=0; i<1000; i++) {
+    output = test(output) == 'true';
+}
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3735,81 +3735,76 @@ struct ScriptCountBlockState
     ~ScriptCountBlockState()
     {
         masm.setPrinter(nullptr);
 
         block.setCode(printer.string());
     }
 };
 
-#ifdef DEBUG
 void
 CodeGenerator::branchIfInvalidated(Register temp, Label *invalidated)
 {
     CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp);
     masm.propagateOOM(ionScriptLabels_.append(label));
 
     // If IonScript::invalidationCount_ != 0, the script has been invalidated.
     masm.branch32(Assembler::NotEqual,
                   Address(temp, IonScript::offsetOfInvalidationCount()),
                   Imm32(0),
                   invalidated);
 }
 
 void
-CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir)
-{
-    if (lir->numDefs() == 0)
-        return;
-
-    MOZ_ASSERT(lir->numDefs() == 1);
-    Register output = ToRegister(lir->getDef(0));
+CodeGenerator::emitAssertObjectOrStringResult(Register input, MIRType type, TemporaryTypeSet *typeset)
+{
+    MOZ_ASSERT(type == MIRType_Object || type == MIRType_ObjectOrNull ||
+               type == MIRType_String || type == MIRType_Symbol);
 
     GeneralRegisterSet regs(GeneralRegisterSet::All());
-    regs.take(output);
+    regs.take(input);
 
     Register temp = regs.takeAny();
     masm.push(temp);
 
     // 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(temp, &done);
 
-    if ((mir->type() == MIRType_Object || mir->type() == MIRType_ObjectOrNull) &&
-        mir->resultTypeSet() &&
-        !mir->resultTypeSet()->unknownObject())
+    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 (mir->type() == MIRType_ObjectOrNull)
-            masm.branchPtr(Assembler::Equal, output, ImmWord(0), &ok);
-        if (mir->resultTypeSet()->getObjectCount() > 0)
-            masm.guardObjectType(output, mir->resultTypeSet(), temp, &miss);
+        if (type == MIRType_ObjectOrNull)
+            masm.branchPtr(Assembler::Equal, input, ImmWord(0), &ok);
+        if (typeset->getObjectCount() > 0)
+            masm.guardObjectType(input, typeset, temp, &miss);
         else
             masm.jump(&miss);
         masm.jump(&ok);
 
         masm.bind(&miss);
-        masm.guardTypeSetMightBeIncomplete(output, temp, &ok);
+        masm.guardTypeSetMightBeIncomplete(input, temp, &ok);
 
         masm.assumeUnreachable("MIR instruction returned object with unexpected type");
 
         masm.bind(&ok);
     }
 
     // Check that we have a valid GC pointer.
     saveVolatile();
     masm.setupUnalignedABICall(2, temp);
     masm.loadJSContext(temp);
     masm.passABIArg(temp);
-    masm.passABIArg(output);
+    masm.passABIArg(input);
 
     void *callee;
-    switch (mir->type()) {
+    switch (type) {
       case MIRType_Object:
         callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectPtr);
         break;
       case MIRType_ObjectOrNull:
         callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectOrNullPtr);
         break;
       case MIRType_String:
         callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidStringPtr);
@@ -3824,80 +3819,99 @@ CodeGenerator::emitObjectOrStringResultC
     masm.callWithABI(callee);
     restoreVolatile();
 
     masm.bind(&done);
     masm.pop(temp);
 }
 
 void
-CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
-{
-    if (lir->numDefs() == 0)
-        return;
-
-    MOZ_ASSERT(lir->numDefs() == BOX_PIECES);
-    if (!lir->getDef(0)->output()->isRegister())
-        return;
-
-    ValueOperand output = ToOutValue(lir);
-
+CodeGenerator::emitAssertResultV(const ValueOperand input, TemporaryTypeSet *typeset)
+{
     GeneralRegisterSet regs(GeneralRegisterSet::All());
-    regs.take(output);
+    regs.take(input);
 
     Register temp1 = regs.takeAny();
     Register temp2 = regs.takeAny();
     masm.push(temp1);
     masm.push(temp2);
 
     // 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 (mir->resultTypeSet() && !mir->resultTypeSet()->unknown()) {
+    if (typeset && !typeset->unknown()) {
         // We have a result TypeSet, assert this value is in it.
         Label miss, ok;
-        masm.guardTypeSet(output, mir->resultTypeSet(), BarrierKind::TypeSet, temp1, &miss);
+        masm.guardTypeSet(input, typeset, BarrierKind::TypeSet, temp1, &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, output, &realMiss);
-        Register payload = masm.extractObject(output, temp1);
+        masm.branchTestObject(Assembler::NotEqual, input, &realMiss);
+        Register payload = masm.extractObject(input, temp1);
         masm.guardTypeSetMightBeIncomplete(payload, temp1, &ok);
         masm.bind(&realMiss);
 
         masm.assumeUnreachable("MIR instruction returned value with unexpected type");
 
         masm.bind(&ok);
     }
 
     // Check that we have a valid GC pointer.
     saveVolatile();
 
-    masm.pushValue(output);
+    masm.pushValue(input);
     masm.movePtr(StackPointer, temp1);
 
     masm.setupUnalignedABICall(2, temp2);
     masm.loadJSContext(temp2);
     masm.passABIArg(temp2);
     masm.passABIArg(temp1);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, AssertValidValue));
-    masm.popValue(output);
+    masm.popValue(input);
     restoreVolatile();
 
     masm.bind(&done);
     masm.pop(temp2);
     masm.pop(temp1);
 }
 
+#ifdef DEBUG
+void
+CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir)
+{
+    if (lir->numDefs() == 0)
+        return;
+
+    MOZ_ASSERT(lir->numDefs() == 1);
+    Register output = ToRegister(lir->getDef(0));
+
+    emitAssertObjectOrStringResult(output, mir->type(), mir->resultTypeSet());
+}
+
+void
+CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
+{
+    if (lir->numDefs() == 0)
+        return;
+
+    MOZ_ASSERT(lir->numDefs() == BOX_PIECES);
+    if (!lir->getDef(0)->output()->isRegister())
+        return;
+
+    ValueOperand output = ToOutValue(lir);
+
+    emitAssertResultV(output, mir->resultTypeSet());
+}
+
 void
 CodeGenerator::emitDebugResultChecks(LInstruction *ins)
 {
     // In debug builds, check that LIR instructions return valid values.
 
     MDefinition *mir = ins->mirRaw();
     if (!mir)
         return;
@@ -9545,16 +9559,32 @@ CodeGenerator::emitAssertRangeD(const Ra
             masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, &notneginf);
             masm.assumeUnreachable("Input shouldn't be -Inf.");
             masm.bind(&notneginf);
         }
     }
 }
 
 void
+CodeGenerator::visitAssertResultV(LAssertResultV *ins)
+{
+    const ValueOperand value = ToValue(ins, LAssertResultV::Input);
+    emitAssertResultV(value, ins->mirRaw()->resultTypeSet());
+}
+
+void
+CodeGenerator::visitAssertResultT(LAssertResultT *ins)
+{
+    Register input = ToRegister(ins->input());
+    MDefinition *mir = ins->mirRaw();
+
+    emitAssertObjectOrStringResult(input, mir->type(), mir->resultTypeSet());
+}
+
+void
 CodeGenerator::visitAssertRangeI(LAssertRangeI *ins)
 {
     Register input = ToRegister(ins->input());
     const Range *r = ins->range();
 
     emitAssertRangeI(r, input);
 }
 
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -351,16 +351,21 @@ class CodeGenerator : public CodeGenerat
     void visitBindNameIC(OutOfLineUpdateCache *ool, DataPtr<BindNameIC> &ic);
     void visitNameIC(OutOfLineUpdateCache *ool, DataPtr<NameIC> &ic);
 
     void visitAssertRangeI(LAssertRangeI *ins);
     void visitAssertRangeD(LAssertRangeD *ins);
     void visitAssertRangeF(LAssertRangeF *ins);
     void visitAssertRangeV(LAssertRangeV *ins);
 
+    void visitAssertResultV(LAssertResultV *ins);
+    void visitAssertResultT(LAssertResultT *ins);
+    void emitAssertResultV(const ValueOperand output, TemporaryTypeSet *typeset);
+    void emitAssertObjectOrStringResult(Register input, MIRType type, TemporaryTypeSet *typeset);
+
     void visitInterruptCheck(LInterruptCheck *lir);
     void visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir);
     void visitRecompileCheck(LRecompileCheck *ins);
 
     IonScriptCounts *extractScriptCounts() {
         IonScriptCounts *counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
         return counts;
@@ -454,19 +459,20 @@ class CodeGenerator : public CodeGenerat
     // Bailout if an element about to be written to is a hole.
     void emitStoreHoleCheck(Register elements, const LAllocation *index, int32_t offsetAdjustment,
                             LSnapshot *snapshot);
 
     void emitAssertRangeI(const Range *r, Register input);
     void emitAssertRangeD(const Range *r, FloatRegister input, FloatRegister temp);
 
     Vector<CodeOffsetLabel, 0, JitAllocPolicy> ionScriptLabels_;
-#ifdef DEBUG
+
     void branchIfInvalidated(Register temp, Label *invalidated);
 
+#ifdef DEBUG
     void emitDebugResultChecks(LInstruction *ins);
     void emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir);
     void emitValueResultChecks(LInstruction *lir, MDefinition *mir);
 #endif
 
     // Script counts created during code generation.
     IonScriptCounts *scriptCounts_;
 
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -69,16 +69,19 @@ JitOptions::JitOptions()
     // are not modified before its OsiPoint.
     SET_DEFAULT(checkOsiPointRegisters, false);
 #endif
 
     // Whether to enable extra code to perform dynamic validation of
     // RangeAnalysis results.
     SET_DEFAULT(checkRangeAnalysis, false);
 
+    // Whether to enable extra code to perform dynamic validations.
+    SET_DEFAULT(runExtraChecks, false);
+
     // Toggle whether eager scalar replacement is globally disabled.
     SET_DEFAULT(disableScalarReplacement, false);
 
     // Toggle whether eager simd unboxing is globally disabled.
     SET_DEFAULT(disableEagerSimdUnbox, false);
 
     // Toggle whether global value numbering is globally disabled.
     SET_DEFAULT(disableGvn, false);
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -41,16 +41,17 @@ LookupRegisterAllocator(const char *name
 
 struct JitOptions
 {
     bool checkGraphConsistency;
 #ifdef CHECK_OSIPOINT_REGISTERS
     bool checkOsiPointRegisters;
 #endif
     bool checkRangeAnalysis;
+    bool runExtraChecks;
     bool disableScalarReplacement;
     bool disableEagerSimdUnbox;
     bool disableGvn;
     bool disableLicm;
     bool disableInlining;
     bool disableEdgeCaseAnalysis;
     bool disableRangeAnalysis;
     bool disableSink;
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -6794,16 +6794,38 @@ class LAssertRangeV : public LInstructio
     MAssertRange *mir() {
         return mir_->toAssertRange();
     }
     const Range *range() {
         return mir()->assertedRange();
     }
 };
 
+class LAssertResultT : public LInstructionHelper<0, 1, 0>
+{
+  public:
+    LIR_HEADER(AssertResultT)
+
+    explicit LAssertResultT(const LAllocation &input) {
+        setOperand(0, input);
+    }
+
+    const LAllocation *input() {
+        return getOperand(0);
+    }
+};
+
+class LAssertResultV : public LInstructionHelper<0, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(AssertResultV)
+
+    static const size_t Input = 0;
+};
+
 class LRecompileCheck : public LInstructionHelper<0, 0, 1>
 {
   public:
     LIR_HEADER(RecompileCheck)
 
     explicit LRecompileCheck(const LDefinition &scratch) {
         setTemp(0, scratch);
     }
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -334,16 +334,18 @@
     _(AsmJSAtomicBinopHeap)         \
     _(AsmJSAtomicBinopHeapForEffect)\
     _(RecompileCheck)               \
     _(MemoryBarrier)                \
     _(AssertRangeI)                 \
     _(AssertRangeD)                 \
     _(AssertRangeF)                 \
     _(AssertRangeV)                 \
+    _(AssertResultV)                \
+    _(AssertResultT)                \
     _(LexicalCheck)                 \
     _(ThrowUninitializedLexical)    \
     _(NurseryObject)                \
     _(Debugger)
 
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/LOpcodes-x86.h"
 #elif defined(JS_CODEGEN_X64)
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -20,16 +20,22 @@
 
 using namespace js;
 using namespace jit;
 
 using mozilla::DebugOnly;
 using JS::GenericNaN;
 
 void
+LIRGenerator::useBoxAtStart(LInstruction *lir, size_t n, MDefinition *mir, LUse::Policy policy)
+{
+    return useBox(lir, n, mir, policy, true);
+}
+
+void
 LIRGenerator::visitCloneLiteral(MCloneLiteral *ins)
 {
     MOZ_ASSERT(ins->type() == MIRType_Object);
     MOZ_ASSERT(ins->input()->type() == MIRType_Object);
 
     LCloneLiteral *lir = new(alloc()) LCloneLiteral(useRegisterAtStart(ins->input()));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
@@ -2312,29 +2318,29 @@ LIRGenerator::visitTypeBarrier(MTypeBarr
 
     MOZ_ASSERT(inputType == outputType);
 
     // Handle typebarrier that will always bail.
     // (Emit LBail for visibility).
     if (ins->alwaysBails()) {
         LBail *bail = new(alloc()) LBail();
         assignSnapshot(bail, Bailout_Inevitable);
+        add(bail, ins);
         redefine(ins, ins->input());
-        add(bail, ins);
         return;
     }
 
     // Handle typebarrier with Value as input.
     if (inputType == MIRType_Value) {
         LDefinition tmp = needTemp ? temp() : tempToUnbox();
         LTypeBarrierV *barrier = new(alloc()) LTypeBarrierV(tmp);
         useBox(barrier, LTypeBarrierV::Input, ins->input());
         assignSnapshot(barrier, Bailout_TypeBarrierV);
+        add(barrier, ins);
         redefine(ins, ins->input());
-        add(barrier, ins);
         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;
@@ -2343,18 +2349,18 @@ LIRGenerator::visitTypeBarrier(MTypeBarr
     {
         needsObjectBarrier = true;
     }
 
     if (needsObjectBarrier) {
         LDefinition tmp = needTemp ? 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));
-        add(barrier, ins);
         return;
     }
 
     // Handle remaining cases: No-op, unbox did everything.
     redefine(ins, ins->getOperand(0));
 }
 
 void
@@ -4067,20 +4073,20 @@ LIRGenerator::visitSimdShift(MSimdShift 
 }
 
 void
 LIRGenerator::visitLexicalCheck(MLexicalCheck *ins)
 {
     MDefinition *input = ins->input();
     MOZ_ASSERT(input->type() == MIRType_Value);
     LLexicalCheck *lir = new(alloc()) LLexicalCheck();
-    redefine(ins, input);
     useBox(lir, LLexicalCheck::Input, input);
     add(lir, ins);
     assignSafepoint(lir, ins);
+    redefine(ins, input);
 }
 
 void
 LIRGenerator::visitThrowUninitializedLexical(MThrowUninitializedLexical *ins)
 {
     LThrowUninitializedLexical *lir = new(alloc()) LThrowUninitializedLexical();
     add(lir, ins);
     assignSafepoint(lir, ins);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -42,19 +42,17 @@ class LIRGenerator : public LIRGenerator
         maxargslots_(0)
     { }
 
     bool generate();
 
   private:
 
     void useBoxAtStart(LInstruction *lir, size_t n, MDefinition *mir,
-                       LUse::Policy policy = LUse::REGISTER) {
-        return useBox(lir, n, mir, policy, true);
-    }
+                       LUse::Policy policy = LUse::REGISTER);
 
     void lowerBitOp(JSOp op, MInstruction *ins);
     void lowerShiftOp(JSOp op, MShiftInstruction *ins);
     void lowerBinaryV(JSOp op, MBinaryInstruction *ins);
     void definePhis();
 
     void lowerCallArguments(MCall *call);
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1130,31 +1130,30 @@ SetDenseElement(JSContext *cx, HandleNat
 }
 
 void
 AutoDetectInvalidation::setReturnOverride()
 {
     cx_->runtime()->jitRuntime()->setIonReturnOverride(rval_.get());
 }
 
-#ifdef DEBUG
 void
 AssertValidObjectPtr(JSContext *cx, JSObject *obj)
 {
     // Check what we can, so that we'll hopefully assert/crash if we get a
     // bogus object (pointer).
     MOZ_ASSERT(obj->compartment() == cx->compartment());
     MOZ_ASSERT(obj->runtimeFromMainThread() == cx->runtime());
 
     MOZ_ASSERT_IF(!obj->hasLazyGroup() && obj->maybeShape(),
                   obj->group()->clasp() == obj->maybeShape()->getObjectClass());
 
     if (obj->isTenured()) {
         MOZ_ASSERT(obj->isAligned());
-        gc::AllocKind kind = obj->asTenured().getAllocKind();
+        mozilla::DebugOnly<gc::AllocKind> kind = obj->asTenured().getAllocKind();
         MOZ_ASSERT(kind <= js::gc::AllocKind::OBJECT_LAST);
         MOZ_ASSERT(obj->asTenured().zone() == cx->zone());
     }
 }
 
 void
 AssertValidObjectOrNullPtr(JSContext *cx, JSObject *obj)
 {
@@ -1175,17 +1174,17 @@ AssertValidStringPtr(JSContext *cx, JSSt
         MOZ_ASSERT(cx->runtime()->isAtomsZone(str->zone()));
     else
         MOZ_ASSERT(str->zone() == cx->zone());
 
     MOZ_ASSERT(str->runtimeFromMainThread() == cx->runtime());
     MOZ_ASSERT(str->isAligned());
     MOZ_ASSERT(str->length() <= JSString::MAX_LENGTH);
 
-    gc::AllocKind kind = str->getAllocKind();
+    mozilla::DebugOnly<gc::AllocKind> kind = str->getAllocKind();
     if (str->isFatInline())
         MOZ_ASSERT(kind == gc::AllocKind::FAT_INLINE_STRING);
     else if (str->isExternal())
         MOZ_ASSERT(kind == gc::AllocKind::EXTERNAL_STRING);
     else if (str->isAtom() || str->isFlat())
         MOZ_ASSERT(kind == gc::AllocKind::STRING || kind == gc::AllocKind::FAT_INLINE_STRING);
     else
         MOZ_ASSERT(kind == gc::AllocKind::STRING);
@@ -1215,17 +1214,16 @@ AssertValidValue(JSContext *cx, Value *v
 {
     if (v->isObject())
         AssertValidObjectPtr(cx, &v->toObject());
     else if (v->isString())
         AssertValidStringPtr(cx, v->toString());
     else if (v->isSymbol())
         AssertValidSymbolPtr(cx, v->toSymbol());
 }
-#endif
 
 bool
 ObjectIsCallable(JSObject *obj)
 {
     return obj->isCallable();
 }
 
 void
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -743,23 +743,21 @@ bool ForcedRecompile(JSContext *cx);
 JSString *RegExpReplace(JSContext *cx, HandleString string, HandleObject regexp,
                         HandleString repl);
 JSString *StringReplace(JSContext *cx, HandleString string, HandleString pattern,
                         HandleString repl);
 
 bool SetDenseElement(JSContext *cx, HandleNativeObject obj, int32_t index, HandleValue value,
                      bool strict);
 
-#ifdef DEBUG
 void AssertValidObjectPtr(JSContext *cx, JSObject *obj);
 void AssertValidObjectOrNullPtr(JSContext *cx, JSObject *obj);
 void AssertValidStringPtr(JSContext *cx, JSString *str);
 void AssertValidSymbolPtr(JSContext *cx, JS::Symbol *sym);
 void AssertValidValue(JSContext *cx, Value *v);
-#endif
 
 void MarkValueFromIon(JSRuntime *rt, Value *vp);
 void MarkStringFromIon(JSRuntime *rt, JSString **stringp);
 void MarkObjectFromIon(JSRuntime *rt, JSObject **objp);
 void MarkShapeFromIon(JSRuntime *rt, Shape **shapep);
 void MarkObjectGroupFromIon(JSRuntime *rt, ObjectGroup **groupp);
 
 // Helper for generatePreBarrier.
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -13,26 +13,16 @@
 #include "jit/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::FloorLog2;
 
 void
-LIRGeneratorARM::useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                        LUse::Policy policy, bool useAtStart)
-{
-    MOZ_ASSERT(mir->type() == MIRType_Value);
-    ensureDefined(mir);
-    lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
-    lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
-}
-
-void
 LIRGeneratorARM::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1,
                              Register reg2)
 {
     MOZ_ASSERT(mir->type() == MIRType_Value);
     MOZ_ASSERT(reg1 != reg2);
 
     ensureDefined(mir);
     lir->setOperand(n, LUse(reg1, mir->virtualRegister()));
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -17,18 +17,16 @@ class LIRGeneratorARM : public LIRGenera
   public:
     LIRGeneratorARM(MIRGenerator *gen, MIRGraph &graph, LIRGraph &lirGraph)
       : LIRGeneratorShared(gen, graph, lirGraph)
     { }
 
   protected:
     // Adds a box input to an instruction, setting operand |n| to the type and
     // |n+1| to the payload.
-    void useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     void useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on ARM all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
     LDefinition tempByteOpRegister();
 
--- a/js/src/jit/mips/Lowering-mips.cpp
+++ b/js/src/jit/mips/Lowering-mips.cpp
@@ -14,27 +14,16 @@
 #include "jit/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::FloorLog2;
 
 void
-LIRGeneratorMIPS::useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                         LUse::Policy policy, bool useAtStart)
-{
-    MOZ_ASSERT(mir->type() == MIRType_Value);
-
-    ensureDefined(mir);
-    lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
-    lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
-}
-
-void
 LIRGeneratorMIPS::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1,
                               Register reg2)
 {
     MOZ_ASSERT(mir->type() == MIRType_Value);
     MOZ_ASSERT(reg1 != reg2);
 
     ensureDefined(mir);
     lir->setOperand(n, LUse(reg1, mir->virtualRegister()));
--- a/js/src/jit/mips/Lowering-mips.h
+++ b/js/src/jit/mips/Lowering-mips.h
@@ -17,18 +17,16 @@ class LIRGeneratorMIPS : public LIRGener
   protected:
     LIRGeneratorMIPS(MIRGenerator *gen, MIRGraph &graph, LIRGraph &lirGraph)
       : LIRGeneratorShared(gen, graph, lirGraph)
     { }
 
   protected:
     // Adds a box input to an instruction, setting operand |n| to the type and
     // |n+1| to the payload.
-    void useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     void useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on MIPS all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
     LDefinition tempByteOpRegister();
 
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -16,20 +16,16 @@ class LIRGeneratorNone : public LIRGener
 {
   public:
     LIRGeneratorNone(MIRGenerator *gen, MIRGraph &graph, LIRGraph &lirGraph)
       : LIRGeneratorShared(gen, graph, lirGraph)
     {
         MOZ_CRASH();
     }
 
-    void useBox(LInstruction *, size_t, MDefinition *,
-                LUse::Policy a = LUse::REGISTER, bool b = false) {
-        MOZ_CRASH();
-    }
     void useBoxFixed(LInstruction *, size_t, MDefinition *, Register, Register) { MOZ_CRASH(); }
 
     LAllocation useByteOpRegister(MDefinition *) { MOZ_CRASH(); }
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *) { MOZ_CRASH(); }
     LDefinition tempByteOpRegister() { MOZ_CRASH(); }
     LDefinition tempToUnbox() { MOZ_CRASH(); }
     bool needTempForPostBarrier() { MOZ_CRASH(); }
     void lowerUntypedPhiInput(MPhi *, uint32_t, LBlock *, size_t) { MOZ_CRASH(); }
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -202,16 +202,42 @@ LIRGeneratorShared::redefine(MDefinition
             emitAtUses(replacement->toInstruction());
         } else {
             replacement = as->toInstruction();
         }
         def->replaceAllUsesWith(replacement);
     } else {
         ensureDefined(as);
         def->setVirtualRegister(as->virtualRegister());
+
+#ifdef DEBUG
+        if (js_JitOptions.runExtraChecks &&
+            def->resultTypeSet() && as->resultTypeSet() &&
+            !def->resultTypeSet()->equals(as->resultTypeSet()))
+        {
+            switch (def->type()) {
+              case MIRType_Object:
+              case MIRType_ObjectOrNull:
+              case MIRType_String:
+              case MIRType_Symbol: {
+                LAssertResultT *check = new(alloc()) LAssertResultT(useRegister(def));
+                add(check, def->toInstruction());
+                break;
+              }
+              case MIRType_Value: {
+                LAssertResultV *check = new(alloc()) LAssertResultV();
+                useBox(check, LAssertRangeV::Input, def);
+                add(check, def->toInstruction());
+                break;
+              }
+              default:
+                break;
+            }
+        }
+#endif
     }
 }
 
 void
 LIRGeneratorShared::ensureDefined(MDefinition *mir)
 {
     if (mir->isEmittedAtUses()) {
         mir->toInstruction()->accept(this);
@@ -494,12 +520,25 @@ LIRGeneratorShared::useRegisterForTypedL
     // int32/bool/double, so we just call useRegister in this case.
     if (type != MIRType_Int32 && type != MIRType_Boolean && type != MIRType_Double)
         return useRegister(mir);
 #endif
 
     return useRegisterAtStart(mir);
 }
 
+void
+LIRGeneratorShared::useBox(LInstruction *lir, size_t n, MDefinition *mir,
+                           LUse::Policy policy, bool useAtStart)
+{
+    MOZ_ASSERT(mir->type() == MIRType_Value);
+
+    ensureDefined(mir);
+    lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
+#if defined(JS_NUNBOX32)
+    lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
+#endif
+}
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_Lowering_shared_inl_h */
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -151,16 +151,20 @@ class LIRGeneratorShared : public MDefin
 
     template <size_t Ops, size_t Temps>
     inline void define(LInstructionHelper<1, Ops, Temps> *lir, MDefinition *mir,
                        LDefinition::Policy policy = LDefinition::REGISTER);
 
     template <size_t Ops, size_t Temps>
     inline void defineReuseInput(LInstructionHelper<1, Ops, Temps> *lir, MDefinition *mir, uint32_t operand);
 
+    // Adds a use at operand |n| of a value-typed insturction.
+    inline void useBox(LInstruction *lir, size_t n, MDefinition *mir,
+                       LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
+
     // Rather than defining a new virtual register, sets |ins| to have the same
     // virtual register as |as|.
     inline void redefine(MDefinition *ins, MDefinition *as);
 
     TempAllocator &alloc() const {
         return graph.alloc();
     }
 
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -10,26 +10,16 @@
 #include "jit/x64/Assembler-x64.h"
 
 #include "jit/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 void
-LIRGeneratorX64::useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                        LUse::Policy policy, bool useAtStart)
-{
-    MOZ_ASSERT(mir->type() == MIRType_Value);
-
-    ensureDefined(mir);
-    lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
-}
-
-void
 LIRGeneratorX64::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register)
 {
     MOZ_ASSERT(mir->type() == MIRType_Value);
 
     ensureDefined(mir);
     lir->setOperand(n, LUse(reg1, mir->virtualRegister()));
 }
 
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -19,18 +19,16 @@ class LIRGeneratorX64 : public LIRGenera
       : LIRGeneratorX86Shared(gen, graph, lirGraph)
     { }
 
   protected:
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     void defineUntypedPhi(MPhi *phi, size_t lirIndex);
 
     // Adds a use at operand |n| of a value-typed insturction.
-    void useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     void useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register);
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on x64 all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
     LDefinition tempByteOpRegister();
 
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -10,27 +10,16 @@
 #include "jit/x86/Assembler-x86.h"
 
 #include "jit/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 void
-LIRGeneratorX86::useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                        LUse::Policy policy, bool useAtStart)
-{
-    MOZ_ASSERT(mir->type() == MIRType_Value);
-
-    ensureDefined(mir);
-    lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
-    lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
-}
-
-void
 LIRGeneratorX86::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1,
                              Register reg2)
 {
     MOZ_ASSERT(mir->type() == MIRType_Value);
     MOZ_ASSERT(reg1 != reg2);
 
     ensureDefined(mir);
     lir->setOperand(n, LUse(reg1, mir->virtualRegister()));
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -17,18 +17,16 @@ class LIRGeneratorX86 : public LIRGenera
   public:
     LIRGeneratorX86(MIRGenerator *gen, MIRGraph &graph, LIRGraph &lirGraph)
       : LIRGeneratorX86Shared(gen, graph, lirGraph)
     { }
 
   protected:
     // Adds a box input to an instruction, setting operand |n| to the type and
     // |n+1| to the payload.
-    void useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     void useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
     // It's a trap! On x86, the 1-byte store can only use one of
     // {al,bl,cl,dl,ah,bh,ch,dh}. That means if the register allocator
     // gives us one of {edi,esi,ebp,esp}, we're out of luck. (The formatter
     // will assert on us.) Ideally, we'd just ask the register allocator to
     // give us one of {al,bl,cl,dl}. For now, just useFixed(al).
     LAllocation useByteOpRegister(MDefinition *mir);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5831,16 +5831,19 @@ SetRuntimeOptions(JSRuntime *rt, const O
             jit::js_JitOptions.disableLoopUnrolling = true;
         else
             return OptionFailure("ion-loop-unrolling", str);
     }
 
     if (op.getBoolOption("ion-check-range-analysis"))
         jit::js_JitOptions.checkRangeAnalysis = true;
 
+    if (op.getBoolOption("ion-extra-checks"))
+        jit::js_JitOptions.runExtraChecks = true;
+
     if (const char *str = op.getStringOption("ion-inlining")) {
         if (strcmp(str, "on") == 0)
             jit::js_JitOptions.disableInlining = false;
         else if (strcmp(str, "off") == 0)
             jit::js_JitOptions.disableInlining = true;
         else
             return OptionFailure("ion-inlining", str);
     }
@@ -6142,16 +6145,18 @@ main(int argc, char **argv, char **envp)
         || !op.addStringOption('\0', "ion-range-analysis", "on/off",
                                "Range analysis (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-sink", "on/off",
                                "Sink code motion (default: off, on to enable)")
         || !op.addStringOption('\0', "ion-loop-unrolling", "on/off",
                                "Loop unrolling (default: off, on to enable)")
         || !op.addBoolOption('\0', "ion-check-range-analysis",
                                "Range analysis checking")
+        || !op.addBoolOption('\0', "ion-extra-checks",
+                               "Perform extra dynamic validation checks")
         || !op.addStringOption('\0', "ion-inlining", "on/off",
                                "Inline methods where possible (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-osr", "on/off",
                                "On-Stack Replacement (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-limit-script-size", "on/off",
                                "Don't compile very large scripts (default: on, off to disable)")
         || !op.addIntOption('\0', "ion-warmup-threshold", "COUNT",
                             "Wait for COUNT calls or iterations before compiling "
--- a/js/src/tests/lib/tests.py
+++ b/js/src/tests/lib/tests.py
@@ -10,17 +10,17 @@ from threading import Thread
 from results import TestOutput
 
 # When run on tbpl, we run each test multiple times with the following
 # arguments.
 TBPL_FLAGS = [
     [], # no flags, normal baseline and ion
     ['--ion-eager', '--ion-offthread-compile=off'], # implies --baseline-eager
     ['--ion-eager', '--ion-offthread-compile=off',
-     '--ion-check-range-analysis', '--no-sse3', '--no-threads'],
+     '--ion-check-range-analysis', '--ion-extra-checks', '--no-sse3', '--no-threads'],
     ['--baseline-eager'],
     ['--baseline-eager', '--no-fpu'],
     ['--no-baseline', '--no-ion'],
 ]
 
 def do_run_cmd(cmd):
     l = [None, None]
     th_run_cmd(cmd, l)