Bug 865059 - Don't analyze scripts until they are compiled by baseline when JM is disabled, r=jandem.
☠☠ backed out by 9c426bd08d28 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 02 May 2013 08:35:01 -0600
changeset 130642 5ac1564bff87ff999bb461ffebceb0a6308c95a2
parent 130641 d8db39f5734946a35325c2999589f9377445a0d6
child 130643 f1fe50632492afca9fde019edb9f34ae5da5d735
push id1579
push userphilringnalda@gmail.com
push dateSat, 04 May 2013 04:38:04 +0000
treeherderfx-team@a56432a42a41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs865059
milestone23.0a1
Bug 865059 - Don't analyze scripts until they are compiled by baseline when JM is disabled, r=jandem.
js/src/frontend/BytecodeEmitter.cpp
js/src/ion/IonFrames.cpp
js/src/jsanalyze.cpp
js/src/jscntxt.h
js/src/jscompartment.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsscript.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/shell/js.cpp
js/src/vm/Xdr.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4362,16 +4362,19 @@ EmitNormalFor(JSContext *cx, BytecodeEmi
     if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 2, bce->offset() - tmp))
         return false;
 
     /* If no loop condition, just emit a loop-closing jump. */
     op = forHead->pn_kid2 ? JSOP_IFNE : JSOP_GOTO;
     if (EmitJump(cx, bce, op, top - bce->offset()) < 0)
         return false;
 
+    if (!bce->tryNoteList.append(JSTRY_LOOP, bce->stackDepth, top, bce->offset()))
+        return false;
+
     /* Now fixup all breaks and continues. */
     return PopStatementBCE(cx, bce);
 }
 
 static inline bool
 EmitFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
     JS_ASSERT(pn->pn_left->isKind(PNK_FORIN) || pn->pn_left->isKind(PNK_FORHEAD));
@@ -4548,16 +4551,19 @@ EmitDo(JSContext *cx, BytecodeEmitter *b
      * Since we use JSOP_IFNE for other purposes as well as for do-while
      * loops, we must store 1 + (beq - top) in the SRC_WHILE note offset,
      * and the decompiler must get that delta and decompile recursively.
      */
     ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
     if (beq < 0)
         return false;
 
+    if (!bce->tryNoteList.append(JSTRY_LOOP, bce->stackDepth, top, bce->offset()))
+        return false;
+
     /*
      * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
      * note gets bigger.
      */
     if (!SetSrcNoteOffset(cx, bce, noteIndex2, 0, beq - top))
         return false;
     if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, 1 + (off - top)))
         return false;
@@ -4604,16 +4610,19 @@ EmitWhile(JSContext *cx, BytecodeEmitter
         return false;
     if (!EmitTree(cx, bce, pn->pn_left))
         return false;
 
     ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
     if (beq < 0)
         return false;
 
+    if (!bce->tryNoteList.append(JSTRY_LOOP, bce->stackDepth, top, bce->offset()))
+        return false;
+
     if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, beq - jmp))
         return false;
 
     return PopStatementBCE(cx, bce);
 }
 
 static bool
 EmitBreak(JSContext *cx, BytecodeEmitter *bce, PropertyName *label)
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -430,16 +430,19 @@ HandleException(JSContext *cx, const Ion
             RootedObject iterObject(cx, &iterValue.toObject());
             if (cx->isExceptionPending())
                 UnwindIteratorForException(cx, iterObject);
             else
                 UnwindIteratorForUncatchableException(cx, iterObject);
             break;
           }
 
+          case JSTRY_LOOP:
+            break;
+
           default:
             JS_NOT_REACHED("Invalid try note");
         }
     }
 
 }
 
 void
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -400,17 +400,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
                 unsigned startOffset = script_->mainOffset + tn->start;
                 if (startOffset == offset + 1) {
                     unsigned catchOffset = startOffset + tn->length;
 
                     /* This will overestimate try block code, for multiple catch/finally. */
                     if (catchOffset > forwardCatch)
                         forwardCatch = catchOffset;
 
-                    if (tn->kind != JSTRY_ITER) {
+                    if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP) {
                         if (!addJump(cx, catchOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
                             return;
                         getCode(catchOffset).exceptionEntry = true;
                         getCode(catchOffset).safePoint = true;
                     }
                 }
             }
             break;
@@ -1502,17 +1502,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
           case JSOP_TRY: {
             JSTryNote *tn = script_->trynotes()->vector;
             JSTryNote *tnlimit = tn + script_->trynotes()->length;
             for (; tn < tnlimit; tn++) {
                 unsigned startOffset = script_->mainOffset + tn->start;
                 if (startOffset == offset + 1) {
                     unsigned catchOffset = startOffset + tn->length;
 
-                    if (tn->kind != JSTRY_ITER) {
+                    if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP) {
                         checkBranchTarget(cx, catchOffset, branchTargets, values, stackDepth);
                         checkExceptionTarget(cx, catchOffset, exceptionTargets);
                     }
                 }
             }
             break;
           }
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1747,16 +1747,17 @@ struct JSContext : js::ContextFriendFiel
 #ifdef JS_METHODJIT
     bool methodJitEnabled;
     bool jitIsBroken;
 
     js::mjit::JaegerRuntime &jaegerRuntime() { return runtime->jaegerRuntime(); }
 #endif
 
     inline bool typeInferenceEnabled() const;
+    inline bool jaegerCompilationAllowed() const;
 
     void updateJITEnabled();
 
 #ifdef MOZ_TRACE_JSCALLS
     /* Function entry/exit debugging callback. */
     JSFunctionCallback    functionCallback;
 
     void doFunctionCallback(const JSFunction *fun,
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -406,16 +406,22 @@ class js::AutoDebugModeGC
 };
 
 inline bool
 JSContext::typeInferenceEnabled() const
 {
     return compartment->zone()->types.inferenceEnabled;
 }
 
+inline bool
+JSContext::jaegerCompilationAllowed() const
+{
+    return compartment->zone()->types.jaegerCompilationAllowed;
+}
+
 inline js::Handle<js::GlobalObject*>
 JSContext::global() const
 {
     /*
      * It's safe to use |unsafeGet()| here because any compartment that is
      * on-stack will be marked automatically, so there's no need for a read
      * barrier on it. Once the compartment is popped, the handle is no longer
      * safe to use.
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2462,20 +2462,22 @@ TypeCompartment::TypeCompartment()
 
 void
 TypeZone::init(JSContext *cx)
 {
     if (!cx ||
         !cx->hasOption(JSOPTION_TYPE_INFERENCE) ||
         !cx->runtime->jitSupportsFloatingPoint)
     {
+        jaegerCompilationAllowed = true;
         return;
     }
 
     inferenceEnabled = true;
+    jaegerCompilationAllowed = cx->hasOption(JSOPTION_METHODJIT);
 }
 
 TypeObject *
 TypeCompartment::newTypeObject(JSContext *cx, Class *clasp, Handle<TaggedProto> proto, bool unknown)
 {
     JS_ASSERT_IF(proto.isObject(), cx->compartment == proto.toObject()->compartment());
 
     TypeObject *object = gc::NewGCThing<TypeObject, CanGC>(cx, gc::FINALIZE_TYPE_OBJECT,
@@ -2687,23 +2689,38 @@ types::UseNewTypeForInitializer(JSContex
      */
 
     if (!cx->typeInferenceEnabled() || (script->function() && !script->treatAsRunOnce))
         return GenericObject;
 
     if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
         return GenericObject;
 
-    AutoEnterAnalysis enter(cx);
-
-    if (!script->ensureRanAnalysis(cx))
-        return GenericObject;
-
-    if (script->analysis()->getCode(pc).inLoop)
-        return GenericObject;
+    /*
+     * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note
+     * indicating their boundary.
+     */
+
+    if (!script->hasTrynotes())
+        return SingletonObject;
+
+    unsigned offset = pc - script->code;
+
+    JSTryNote *tn = script->trynotes()->vector;
+    JSTryNote *tnlimit = tn + script->trynotes()->length;
+    for (; tn < tnlimit; tn++) {
+        if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP)
+            continue;
+
+        unsigned startOffset = script->mainOffset + tn->start;
+        unsigned endOffset = startOffset + tn->length;
+
+        if (offset >= startOffset && offset < endOffset)
+            return GenericObject;
+    }
 
     return SingletonObject;
 }
 
 NewObjectKind
 types::UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc, Class *clasp)
 {
     return UseNewTypeForInitializer(cx, script, pc, JSCLASS_CACHED_PROTO_KEY(clasp));
@@ -5606,18 +5623,26 @@ types::MarkIteratorUnknownSlow(JSContext
     jsbytecode *pc;
     RootedScript script(cx, cx->stack.currentScript(&pc));
     if (!script || !pc)
         return;
 
     if (JSOp(*pc) != JSOP_ITER)
         return;
 
+    if (IgnoreTypeChanges(cx, script))
+        return;
+
     AutoEnterAnalysis enter(cx);
 
+    if (!script->ensureRanAnalysis(cx)) {
+        cx->compartment->types.setPendingNukeTypes(cx);
+        return;
+    }
+
     /*
      * This script is iterating over an actual Iterator or Generator object, or
      * an object with a custom __iterator__ hook. In such cases 'for in' loops
      * can produce values other than strings, and the types of the ITER opcodes
      * in the script need to be updated. During analysis this is done with the
      * forTypes in the analysis state, but we don't keep a pointer to this type
      * set and need to scan the script to fix affected opcodes.
      */
@@ -5691,16 +5716,20 @@ IsAboutToBeFinalized(TypeObjectKey *key)
     JS_ASSERT(tmp == reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1));
     return isAboutToBeFinalized;
 }
 
 void
 types::TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
+
+    if (IgnoreTypeChanges(cx, script))
+        return;
+
     AutoEnterAnalysis enter(cx);
 
     /* Directly update associated type sets for applicable bytecodes. */
     if (js_CodeSpec[*pc].format & JOF_TYPESET) {
         if (!script->ensureRanAnalysis(cx)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
@@ -5796,16 +5825,19 @@ types::TypeDynamicResult(JSContext *cx, 
 
 void
 types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
 {
     /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
     if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
         return;
 
+    if (IgnoreTypeChanges(cx, script))
+        return;
+
     AutoEnterAnalysis enter(cx);
 
     if (!script->ensureRanAnalysis(cx)) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
     Type type = GetValueType(cx, rval);
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -1461,16 +1461,23 @@ struct TypeZone
      * Bit set if all current types must be marked as unknown, and all scripts
      * recompiled. Caused by OOM failure within inference operations.
      */
     bool                         pendingNukeTypes;
 
     /* Whether type inference is enabled in this compartment. */
     bool                         inferenceEnabled;
 
+    /*
+     * JM compilation is allowed only if script analysis has been used to
+     * monitor the behavior of all scripts in this zone since its creation.
+     * OSR in JM requires this property.
+     */
+    bool jaegerCompilationAllowed;
+
     TypeZone(JS::Zone *zone);
     ~TypeZone();
     void init(JSContext *cx);
 
     JS::Zone *zone() const { return zone_; }
 
     void sweep(FreeOp *fop, bool releaseTypes);
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -568,29 +568,46 @@ void MarkIteratorUnknownSlow(JSContext *
  */
 inline void
 MarkIteratorUnknown(JSContext *cx)
 {
     if (cx->typeInferenceEnabled())
         MarkIteratorUnknownSlow(cx);
 }
 
+/*
+ * Return whether new type information in the specified script should be
+ * ignored. Tracking new types in a script requires the script to be analyzed,
+ * which can consume a large amount of memory when dealing with scripts that
+ * don't run long enough to be compiled (as is the case for the majority of
+ * executed scripts in web code).
+ */
+inline bool
+IgnoreTypeChanges(JSContext *cx, JSScript *script)
+{
+    return !script->hasAnalysis() &&
+           !cx->jaegerCompilationAllowed() &&
+           script->analyzedArgsUsage();
+}
+
 void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args,
                          bool constructing);
 
 /*
  * Monitor a javascript call, either on entry to the interpreter or made
  * from within the interpreter.
  */
 inline bool
 TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
 {
     if (args.callee().isFunction()) {
         JSFunction *fun = args.callee().toFunction();
         if (fun->isInterpreted()) {
+            if (IgnoreTypeChanges(cx, fun->nonLazyScript()))
+                return true;
             if (!fun->nonLazyScript()->ensureRanAnalysis(cx))
                 return false;
             if (cx->typeInferenceEnabled())
                 TypeMonitorCallSlow(cx, &args.callee(), args, constructing);
         }
     }
 
     return true;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -560,19 +560,21 @@ js::ExecuteKernel(JSContext *cx, HandleS
             result->setUndefined();
         return true;
     }
 
     ExecuteFrameGuard efg;
     if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg))
         return false;
 
-    if (!script->ensureRanAnalysis(cx))
-        return false;
-    TypeScript::SetThis(cx, script, efg.fp()->thisValue());
+    if (!types::IgnoreTypeChanges(cx, script)) {
+        if (!script->ensureRanAnalysis(cx))
+            return false;
+        TypeScript::SetThis(cx, script, efg.fp()->thisValue());
+    }
 
     Probes::startExecution(script);
     bool ok = RunScript(cx, efg.fp());
     Probes::stopExecution(script);
 
     /* Propgate the return value out. */
     if (result)
         *result = efg.fp()->returnValue();
@@ -3361,17 +3363,21 @@ END_CASE(JSOP_ARRAYPUSH)
                 /* This is similar to JSOP_ENDITER in the interpreter loop. */
                 JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER);
                 RootedObject &obj = rootObject0;
                 obj = &regs.sp[-1].toObject();
                 bool ok = UnwindIteratorForException(cx, obj);
                 regs.sp -= 1;
                 if (!ok)
                     goto error;
+                break;
               }
+
+              case JSTRY_LOOP:
+                break;
            }
         }
 
         /*
          * Propagate the exception or error to the caller unless the exception
          * is an asynchronous return from a generator.
          */
         interpReturnOK = false;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1515,18 +1515,22 @@ js::CreateThisForFunctionWithProto(JSCon
             return NULL;
         res = CreateThisForFunctionWithType(cx, type, callee->getParent(), newKind);
     } else {
         gc::AllocKind allocKind = NewObjectGCKind(&ObjectClass);
         res = NewObjectWithClassProto(cx, &ObjectClass, proto, callee->getParent(), allocKind, newKind);
     }
 
     if (res && cx->typeInferenceEnabled()) {
-        RootedScript script(cx, callee->toFunction()->nonLazyScript());
-        TypeScript::SetThis(cx, script, types::Type::ObjectType(res));
+        JSScript *script = callee->toFunction()->nonLazyScript();
+        if (!types::IgnoreTypeChanges(cx, script)) {
+            if (!script->ensureRanAnalysis(cx))
+                return NULL;
+            TypeScript::SetThis(cx, script, types::Type::ObjectType(res));
+        }
     }
 
     return res;
 }
 
 JSObject *
 js::CreateThisForFunction(JSContext *cx, HandleObject callee, bool newType)
 {
@@ -1542,18 +1546,22 @@ js::CreateThisForFunction(JSContext *cx,
     JSObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind);
 
     if (obj && newType) {
         RootedObject nobj(cx, obj);
 
         /* Reshape the singleton before passing it as the 'this' value. */
         JSObject::clear(cx, nobj);
 
-        RootedScript calleeScript(cx, callee->toFunction()->nonLazyScript());
-        TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
+        JSScript *calleeScript = callee->toFunction()->nonLazyScript();
+        if (!IgnoreTypeChanges(cx, calleeScript)) {
+            if (!calleeScript->ensureRanAnalysis(cx))
+                return NULL;
+            TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
+        }
 
         return nobj;
     }
 
     return obj;
 }
 
 /*
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -44,34 +44,37 @@ namespace mjit {
 namespace analyze {
     class ScriptAnalysis;
 }
 
 }
 
 /*
  * Type of try note associated with each catch or finally block, and also with
- * for-in loops.
+ * for-in and other kinds of loops. Non-for-in loops do not need these notes
+ * for exception unwinding, but storing their boundaries here is helpful for
+ * heuristics that need to know whether a given op is inside a loop.
  */
 typedef enum JSTryNoteKind {
     JSTRY_CATCH,
     JSTRY_FINALLY,
-    JSTRY_ITER
+    JSTRY_ITER,
+    JSTRY_LOOP
 } JSTryNoteKind;
 
 /*
  * Exception handling record.
  */
 struct JSTryNote {
     uint8_t         kind;       /* one of JSTryNoteKind */
     uint8_t         padding;    /* explicit padding on uint16_t boundary */
     uint16_t        stackDepth; /* stack depth upon exception handler entry */
-    uint32_t        start;      /* start of the try statement or for-in loop
+    uint32_t        start;      /* start of the try statement or loop
                                    relative to script->main */
-    uint32_t        length;     /* length of the try statement or for-in loop */
+    uint32_t        length;     /* length of the try statement or loop */
 };
 
 namespace js {
 
 struct ConstArray {
     js::HeapValue   *vector;    /* array of indexed constant values */
     uint32_t        length;
 };
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -116,16 +116,18 @@ mjit::Compiler::Compiler(JSContext *cx, 
     debugMode_(cx->compartment->debugMode()),
     inlining_(false),
     hasGlobalReallocation(false),
     oomInVector(false),
     overflowICSpace(false),
     gcNumber(cx->runtime->gcNumber),
     pcLengths(NULL)
 {
+    JS_ASSERT(cx->jaegerCompilationAllowed());
+
     if (!IsIonEnabled(cx)) {
         /* Once a script starts getting really hot we will inline calls in it. */
         if (!debugMode() && cx->typeInferenceEnabled() && globalObj &&
             (outerScript->getUseCount() >= USES_BEFORE_INLINING ||
              cx->hasOption(JSOPTION_METHODJIT_ALWAYS))) {
             inlining_ = true;
         }
     }
@@ -989,16 +991,19 @@ CompileStatus
 mjit::CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
                    bool construct, CompileRequest request, StackFrame *frame)
 {
     bool compiledOnce = false;
   checkOutput:
     if (!cx->methodJitEnabled)
         return Compile_Abort;
 
+    if (!cx->jaegerCompilationAllowed())
+        return Compile_Abort;
+
 #ifdef JS_ION
     if (ion::IsBaselineEnabled(cx) || ion::IsEnabled(cx))
         return Compile_Abort;
 #endif
 
     /*
      * If SPS (profiling) is enabled, then the emitted instrumentation has to be
      * careful to not wildly write to random locations. This is relevant
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -110,17 +110,21 @@ FindExceptionHandler(JSContext *cx)
                    * pending exception.
                    */
                   JS_ASSERT(JSOp(*pc) == JSOP_ENDITER);
                   RootedObject obj(cx, &cx->regs().sp[-1].toObject());
                   bool ok = UnwindIteratorForException(cx, obj);
                   cx->regs().sp -= 1;
                   if (!ok)
                       goto error;
+                  break;
                 }
+
+                case JSTRY_LOOP:
+                  break;
             }
         }
     } else {
         UnwindForUncatchableException(cx, cx->regs());
     }
 
     return NULL;
 }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4922,21 +4922,16 @@ ProcessArgs(JSContext *cx, JSObject *obj
     if (op->getBoolOption('w'))
         reportWarnings = JS_TRUE;
     else if (op->getBoolOption('W'))
         reportWarnings = JS_FALSE;
 
     if (op->getBoolOption('s'))
         JS_ToggleOptions(cx, JSOPTION_STRICT);
 
-    if (op->getBoolOption("no-jm")) {
-        enableMethodJit = false;
-        JS_ToggleOptions(cx, JSOPTION_METHODJIT);
-    }
-
     if (op->getBoolOption('d')) {
         JS_SetRuntimeDebugMode(JS_GetRuntime(cx), true);
         JS_SetDebugMode(cx, true);
     }
 
     if (op->getBoolOption('b'))
         printTiming = true;
 
@@ -5116,23 +5111,27 @@ ProcessArgs(JSContext *cx, JSObject *obj
 }
 
 int
 Shell(JSContext *cx, OptionParser *op, char **envp)
 {
     JSAutoRequest ar(cx);
 
     /*
-     * First check to see if type inference is enabled. This flag must be set
-     * on the compartment when it is constructed.
+     * First check to see if type inference and JM are enabled. These flags
+     * must be set on the compartment when it is constructed.
      */
     if (op->getBoolOption("no-ti")) {
         enableTypeInference = false;
         JS_ToggleOptions(cx, JSOPTION_TYPE_INFERENCE);
     }
+    if (op->getBoolOption("no-jm")) {
+        enableMethodJit = false;
+        JS_ToggleOptions(cx, JSOPTION_METHODJIT);
+    }
 
     RootedObject glob(cx);
     glob = NewGlobalObject(cx, NULL);
     if (!glob)
         return 1;
 
     JS_SetGlobalObject(cx, glob);
 
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -21,17 +21,17 @@ namespace js {
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number is XDR'd near the front of xdr bytecode and
  * aborts deserialization if there is a mismatch between the current
  * and saved versions. If deserialization fails, the data should be
  * invalidated if possible.
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 141);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 142);
 
 class XDRBuffer {
   public:
     XDRBuffer(JSContext *cx)
       : context(cx), base(NULL), cursor(NULL), limit(NULL) { }
 
     JSContext *cx() const {
         return context;