Bug 742163 - Clean up JSScript::jitArityCheck{Normal,Ctor}. r=dvander.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 05 Apr 2012 00:02:02 -0700
changeset 94380 55ba87dd42e26d724283d2cc91a3fdf9f641c268
parent 94379 e618946cad01c7581bc62e7e828be198ebceee2f
child 94381 26eb0d5a95a643a96e1b8b1f830809471aa670db
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs742163
milestone14.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 742163 - Clean up JSScript::jitArityCheck{Normal,Ctor}. r=dvander.
js/src/jsinfer.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/MonoIC.cpp
js/src/methodjit/Retcon.cpp
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2200,25 +2200,25 @@ TypeCompartment::addPendingRecompile(JSC
 
 void
 TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
 #ifdef JS_METHODJIT
     RecompileInfo info;
     info.script = script;
 
-    if (script->jitNormal) {
+    if (script->jitHandleNormal.isValid()) {
         info.constructing = false;
-        info.chunkIndex = script->jitNormal->chunkIndex(pc);
+        info.chunkIndex = script->jitHandleNormal.getValid()->chunkIndex(pc);
         addPendingRecompile(cx, info);
     }
 
-    if (script->jitCtor) {
+    if (script->jitHandleCtor.isValid()) {
         info.constructing = true;
-        info.chunkIndex = script->jitCtor->chunkIndex(pc);
+        info.chunkIndex = script->jitHandleCtor.getValid()->chunkIndex(pc);
         addPendingRecompile(cx, info);
     }
 #endif
 }
 
 void
 TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
                                  bool returnOnly)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1684,17 +1684,17 @@ JSScript::ensureHasDebug(JSContext *cx)
 
     return true;
 }
 
 void
 JSScript::recompileForStepMode(FreeOp *fop)
 {
 #ifdef JS_METHODJIT
-    if (jitNormal || jitCtor) {
+    if (hasJITCode()) {
         mjit::Recompiler::clearStackReferences(fop, this);
         mjit::ReleaseScriptCode(fop, this);
     }
 #endif
 }
 
 bool
 JSScript::tryNewStepMode(JSContext *cx, uint32_t newValue)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -267,19 +267,23 @@ class Bindings
 #define JS_OBJECT_ARRAY_SIZE(length)                                          \
     (offsetof(JSObjectArray, vector) + sizeof(JSObject *) * (length))
 
 #ifdef JS_METHODJIT
 namespace JSC {
     class ExecutablePool;
 }
 
-#define JS_UNJITTABLE_SCRIPT (reinterpret_cast<void*>(1))
+namespace js {
+namespace mjit {
+    struct JITScript;
+    class CallCompiler;
+}
+}
 
-namespace js { namespace mjit { struct JITScript; } }
 #endif
 
 namespace js {
 
 namespace analyze { class ScriptAnalysis; }
 
 class ScriptCounts
 {
@@ -338,28 +342,71 @@ class DebugScript
 static const uint32_t JS_SCRIPT_COOKIE = 0xc00cee;
 
 struct JSScript : public js::gc::Cell
 {
   private:
     static const uint32_t stepFlagMask = 0x80000000U;
     static const uint32_t stepCountMask = 0x7fffffffU;
 
-  /*
-   * We order fields according to their size in order to avoid wasting space
-   * for alignment.
-   */
+  public:
+    // This type wraps JITScript.  It has three possible states.
+    // - "Empty": no compilation has been attempted and there is no JITScript.
+    // - "Unjittable": compilation failed and there is no JITScript.
+    // - "Valid": compilation succeeded and there is a JITScript.
+    class JITScriptHandle
+    {
+        // CallCompiler must be a friend because it generates code that uses
+        // UNJITTABLE.
+        friend class js::mjit::CallCompiler;
+
+        // The exact representation:
+        // - NULL means "empty".
+        // - UNJITTABLE means "unjittable".
+        // - Any other value means "valid".
+        // UNJITTABLE = 1 so that we can check that a JITScript is valid
+        // with a single |> 1| test.  It's defined outside the class because
+        // non-integral static const fields can't be defined in the class.
+        static const js::mjit::JITScript *UNJITTABLE;   // = (JITScript *)1;
+        js::mjit::JITScript *value;
+
+      public:
+        JITScriptHandle()       { value = NULL; }
 
-  /* Larger-than-word-sized fields. */
+        bool isEmpty()          { return value == NULL; }
+        bool isUnjittable()     { return value == UNJITTABLE; }
+        bool isValid()          { return value  > UNJITTABLE; }
+
+        js::mjit::JITScript *getValid() {
+            JS_ASSERT(isValid());
+            return value;
+        }
+
+        void setEmpty()         { value = NULL; }
+        void setUnjittable()    { value = const_cast<js::mjit::JITScript *>(UNJITTABLE); }
+        void setValid(js::mjit::JITScript *jit) {
+            value = jit;
+            JS_ASSERT(isValid());
+        }
+
+        static void staticAsserts();
+    };
+
+    //
+    // We order fields according to their size in order to avoid wasting space
+    // for alignment.
+    //
+
+    // Larger-than-word-sized fields.
 
   public:
     js::Bindings    bindings;   /* names of top-level variables in this script
                                    (and arguments if this is a function script) */
 
-  /* Word-sized fields. */
+    // Word-sized fields.
 
   public:
     jsbytecode      *code;      /* bytecodes and their immediate operands */
     uint8_t         *data;      /* pointer to variable-length data array (see 
                                    comment above NewScript() for details) */
 
     const char      *filename;  /* source filename or null */
     JSAtom          **atoms;    /* maps immediate index to literal struct */
@@ -383,96 +430,86 @@ struct JSScript : public js::gc::Cell
     js::HeapPtr<js::GlobalObject, JSScript*> globalObject;
 
     /* Execution and profiling information for JIT code in the script. */
     js::ScriptCounts scriptCounts;
 
     /* Persistent type information retained across GCs. */
     js::types::TypeScript *types;
 
+  public:
 #ifdef JS_METHODJIT
-    // Fast-cached pointers to make calls faster. These are also used to
-    // quickly test whether there is JIT code; a NULL value means no
-    // compilation has been attempted. A JS_UNJITTABLE_SCRIPT value means
-    // compilation failed. Any value is the arity-check entry point.
-    void *jitArityCheckNormal;
-    void *jitArityCheckCtor;
-
-    js::mjit::JITScript *jitNormal;   /* Extra JIT info for normal scripts */
-    js::mjit::JITScript *jitCtor;     /* Extra JIT info for constructors */
+    JITScriptHandle jitHandleNormal; // extra JIT info for normal scripts
+    JITScriptHandle jitHandleCtor;   // extra JIT info for constructors
 #endif
 
   private:
     js::DebugScript     *debug;
     js::HeapPtrFunction function_;
 
     size_t          useCount;   /* Number of times the script has been called
                                  * or has had backedges taken. Reset if the
                                  * script's JIT code is forcibly discarded. */
 #if JS_BITS_PER_WORD == 32
     void *padding_;
 #endif
 
-    /* 32-bit fields. */
+    // 32-bit fields.
 
   public:
     uint32_t        length;     /* length of code vector */
 
     uint32_t        lineno;     /* base line number of script */
 
     uint32_t        mainOffset; /* offset of main entry point from code, after
                                    predef'ing prolog */
 
     uint32_t        natoms;     /* length of atoms array */
 
 #ifdef DEBUG
-    /*
-     * Unique identifier within the compartment for this script, used for
-     * printing analysis information.
-     */
+    // Unique identifier within the compartment for this script, used for
+    // printing analysis information.
     uint32_t        id_;
   private:
     uint32_t        idpad;
   public:
 #endif
 
-    /* 16-bit fields. */
+    // 16-bit fields.
 
   private:
     uint16_t        version;    /* JS version under which script was compiled */
 
   public:
     uint16_t        nfixed;     /* number of slots besides stack operands in
                                    slot array */
 
     uint16_t        nTypeSets;  /* number of type sets used in this script for
                                    dynamic type monitoring */
 
     uint16_t        nslots;     /* vars plus maximum stack depth */
     uint16_t        staticLevel;/* static level for display maintenance */
 
-    /* 8-bit fields. */
+    // 8-bit fields.
 
-    /*
-     * Offsets to various array structures from the end of this script, or
-     * JSScript::INVALID_OFFSET if the array has length 0.
-     */
   public:
+    // Offsets to various array structures from the end of this script, or
+    // JSScript::INVALID_OFFSET if the array has length 0.
     uint8_t         constsOffset;   /* offset to the array of constants */
     uint8_t         objectsOffset;  /* offset to the array of nested function,
                                        block, scope, xml and one-time regexps
                                        objects */
     uint8_t         regexpsOffset;  /* offset to the array of to-be-cloned
                                        regexps  */
     uint8_t         trynotesOffset; /* offset to the array of try notes */
     uint8_t         globalsOffset;  /* offset to the array of global slots */
     uint8_t         closedArgsOffset; /* offset to the array of closed args */
     uint8_t         closedVarsOffset; /* offset to the array of closed vars */
 
-    /* 1-bit fields. */
+    // 1-bit fields.
 
   public:
     bool            noScriptRval:1; /* no need for result value of last
                                        expression statement */
     bool            savedCallerFun:1; /* can call getCallerFunction() */
     bool            strictModeCode:1; /* code is in strict mode */
     bool            compileAndGo:1;   /* script was compiled with TCF_COMPILE_N_GO */
     bool            usesEval:1;       /* script uses eval() */
@@ -509,17 +546,19 @@ struct JSScript : public js::gc::Cell
      * argument objects creation, we maintain the invariant that needsArgsObj()
      * is only queried after this analysis has occurred (analyzedArgsUsage()).
      */
   private:
     bool            mayNeedArgsObj_:1;
     bool            analyzedArgsUsage_:1;
     bool            needsArgsObj_:1;
 
-    /* End of fields.  Start methods. */
+    //
+    // End of fields.  Start methods.
+    //
 
     /*
      * Two successively less primitive ways to make a new JSScript.  The first
      * does *not* call a non-null cx->runtime->newScriptHook -- only the second,
      * NewScriptFromEmitter, calls this optional debugger hook.
      *
      * The NewScript function can't know whether the script it creates belongs
      * to a function, or is top-level or eval code, but the debugger wants access
@@ -596,44 +635,60 @@ struct JSScript : public js::gc::Cell
     /* Return creation time global or null. */
     js::GlobalObject *getGlobalObjectOrNull() const {
         return isCachedEval ? NULL : globalObject.get();
     }
 
   private:
     bool makeTypes(JSContext *cx);
     bool makeAnalysis(JSContext *cx);
-  public:
 
 #ifdef JS_METHODJIT
-    bool hasJITCode() {
-        return jitNormal || jitCtor;
+  private:
+    // CallCompiler must be a friend because it generates code that directly
+    // accesses jitHandleNormal/jitHandleCtor, via jitHandleOffset().
+    friend class js::mjit::CallCompiler;
+
+    static size_t jitHandleOffset(bool constructing) {
+        return constructing ? offsetof(JSScript, jitHandleCtor)
+                            : offsetof(JSScript, jitHandleNormal);
     }
 
+  public:
+    bool hasJITCode()   { return jitHandleNormal.isValid() || jitHandleCtor.isValid(); }
+
+    JITScriptHandle *jitHandle(bool constructing) {
+        return constructing ? &jitHandleCtor : &jitHandleNormal;
+    }
+
+    js::mjit::JITScript *getJIT(bool constructing) {
+        JITScriptHandle *jith = jitHandle(constructing);
+        return jith->isValid() ? jith->getValid() : NULL;
+    }
+
+    static void ReleaseCode(js::FreeOp *fop, JITScriptHandle *jith);
+
     // These methods are implemented in MethodJIT.h.
     inline void **nativeMap(bool constructing);
     inline void *nativeCodeForPC(bool constructing, jsbytecode *pc);
 
-    js::mjit::JITScript *getJIT(bool constructing) {
-        return constructing ? jitCtor : jitNormal;
-    }
-
     size_t getUseCount() const  { return useCount; }
     size_t incUseCount() { return ++useCount; }
     size_t *addressOfUseCount() { return &useCount; }
     void resetUseCount() { useCount = 0; }
 
     /*
      * Size of the JITScript and all sections.  If |mallocSizeOf| is NULL, the
      * size is computed analytically.  (This method is implemented in
      * MethodJIT.cpp.)
      */
     size_t sizeOfJitScripts(JSMallocSizeOfFun mallocSizeOf);
 #endif
 
+  public:
     js::PCCounts getPCCounts(jsbytecode *pc) {
         JS_ASSERT(size_t(pc - code) < length);
         return scriptCounts.pcCountsVector[pc - code];
     }
 
     bool initScriptCounts(JSContext *cx);
     void destroyScriptCounts(js::FreeOp *fop);
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -138,23 +138,22 @@ mjit::Compiler::Compiler(JSContext *cx, 
     }
 }
 
 CompileStatus
 mjit::Compiler::compile()
 {
     JS_ASSERT(!outerChunkRef().chunk);
 
-    void **checkAddr = isConstructing
-                       ? &outerScript->jitArityCheckCtor
-                       : &outerScript->jitArityCheckNormal;
-
     CompileStatus status = performCompilation();
     if (status != Compile_Okay && status != Compile_Retry) {
-        *checkAddr = JS_UNJITTABLE_SCRIPT;
+        JSScript::JITScriptHandle *jith = outerScript->jitHandle(isConstructing);
+        JSScript::ReleaseCode(cx->runtime->defaultFreeOp(), jith);
+        jith->setUnjittable();
+
         if (outerScript->function()) {
             outerScript->uninlineable = true;
             types::MarkTypeObjectFlags(cx, outerScript->function(),
                                        types::OBJECT_FLAG_UNINLINEABLE);
         }
     }
 
     return status;
@@ -653,25 +652,23 @@ static uint32_t CHUNK_LIMIT = 1500;
 void
 mjit::SetChunkLimit(uint32_t limit)
 {
     if (limit)
         CHUNK_LIMIT = limit;
 }
 
 JITScript *
-MakeJITScript(JSContext *cx, JSScript *script, bool construct)
+MakeJITScript(JSContext *cx, JSScript *script)
 {
     if (!script->ensureRanAnalysis(cx, NULL))
         return NULL;
 
     ScriptAnalysis *analysis = script->analysis();
 
-    JITScript *&location = construct ? script->jitCtor : script->jitNormal;
-
     Vector<ChunkDescriptor> chunks(cx);
     Vector<CrossChunkEdge> edges(cx);
 
     if (script->length < CHUNK_LIMIT || !cx->typeInferenceEnabled()) {
         ChunkDescriptor desc;
         desc.begin = 0;
         desc.end = script->length;
         if (!chunks.append(desc))
@@ -890,20 +887,18 @@ MakeJITScript(JSContext *cx, JSScript *s
         b.end = a.end;
 
         if (chunks.length() == 1) {
             /* Seed the chunk's count so it is immediately compiled. */
             b.counter = INFER_USES_BEFORE_COMPILE;
         }
     }
 
-    if (edges.empty()) {
-        location = jit;
+    if (edges.empty())
         return jit;
-    }
 
     jit->nedges = edges.length();
     CrossChunkEdge *jitEdges = jit->edges();
     for (unsigned i = 0; i < edges.length(); i++) {
         const CrossChunkEdge &a = edges[i];
         CrossChunkEdge &b = jitEdges[i];
         b.source = a.source;
         b.target = a.target;
@@ -932,54 +927,55 @@ MakeJITScript(JSContext *cx, JSScript *s
     JaegerSpew(JSpew_PICs, "generated SHIM POOL stub %p (%lu bytes)\n",
                shimCode, (unsigned long)masm.size());
 
     for (unsigned i = 0; i < jit->nedges; i++) {
         CrossChunkEdge &edge = jitEdges[i];
         edge.shimLabel = shimCode + (size_t) edge.shimLabel;
     }
 
-    location = jit;
     return jit;
 }
 
 CompileStatus
 mjit::CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
                    bool construct, CompileRequest request)
 {
   restart:
     if (!cx->methodJitEnabled)
         return Compile_Abort;
 
-    void *addr = construct ? script->jitArityCheckCtor : script->jitArityCheckNormal;
-    if (addr == JS_UNJITTABLE_SCRIPT)
+    JSScript::JITScriptHandle *jith = script->jitHandle(construct);
+    if (jith->isUnjittable())
         return Compile_Abort;
 
-    JITScript *jit = script->getJIT(construct);
-
     if (request == CompileRequest_Interpreter &&
         !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
         (cx->typeInferenceEnabled()
          ? script->incUseCount() <= INFER_USES_BEFORE_COMPILE
          : script->incUseCount() <= USES_BEFORE_COMPILE))
     {
         return Compile_Skipped;
     }
 
     if (!cx->compartment->ensureJaegerCompartmentExists(cx))
         return Compile_Error;
 
     // Ensure that constructors have at least one slot.
     if (construct && !script->nslots)
         script->nslots++;
 
-    if (!jit) {
-        jit = MakeJITScript(cx, script, construct);
+    JITScript *jit;
+    if (jith->isEmpty()) {
+        jit = MakeJITScript(cx, script);
         if (!jit)
             return Compile_Error;
+        jith->setValid(jit);
+    } else {
+        jit = jith->getValid();
     }
     unsigned chunkIndex = jit->chunkIndex(pc);
     ChunkDescriptor &desc = jit->chunkDescriptor(chunkIndex);
 
     if (desc.chunk)
         return Compile_Okay;
 
     if (request == CompileRequest_Interpreter &&
@@ -1373,18 +1369,16 @@ mjit::Compiler::finishThisUp()
     chunk->pcLengths = pcLengths;
 
     if (chunkIndex == 0) {
         jit->invokeEntry = result;
         if (script->function()) {
             jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress();
             jit->argsCheckEntry = stubCode.locationOf(argsCheckLabel).executableAddress();
             jit->fastEntry = fullCode.locationOf(invokeLabel).executableAddress();
-            void *&addr = isConstructing ? script->jitArityCheckCtor : script->jitArityCheckNormal;
-            addr = jit->arityCheckEntry;
         }
     }
 
     /*
      * WARNING: mics(), callICs() et al depend on the ordering of these
      * variable-length sections.  See JITChunk's declaration for details.
      */
 
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1357,47 +1357,55 @@ JITScript::destroyChunk(FreeOp *fop, uns
     if (chunkIndex == 0) {
         if (argsCheckPool) {
             argsCheckPool->release();
             argsCheckPool = NULL;
         }
 
         invokeEntry = NULL;
         fastEntry = NULL;
+        argsCheckEntry = NULL;
         arityCheckEntry = NULL;
-        argsCheckEntry = NULL;
-
-        if (script->jitNormal == this)
-            script->jitArityCheckNormal = NULL;
-        else
-            script->jitArityCheckCtor = NULL;
 
         // Fixup any ICs still referring to this chunk.
         while (!JS_CLIST_IS_EMPTY(&callers)) {
             JS_STATIC_ASSERT(offsetof(ic::CallICInfo, links) == 0);
             ic::CallICInfo *ic = (ic::CallICInfo *) callers.next;
 
             uint8_t *start = (uint8_t *)ic->funGuard.executableAddress();
             JSC::RepatchBuffer repatch(JSC::JITCode(start - 32, 64));
 
             repatch.repatch(ic->funGuard, NULL);
             repatch.relink(ic->funJump, ic->slowPathStart);
             ic->purgeGuardedObject();
         }
     }
 }
 
+const js::mjit::JITScript *JSScript::JITScriptHandle::UNJITTABLE =
+    reinterpret_cast<js::mjit::JITScript *>(1);
+
+void
+JSScript::JITScriptHandle::staticAsserts()
+{
+    // JITScriptHandle's memory layout must match that of JITScript *.
+    JS_STATIC_ASSERT(sizeof(JSScript::JITScriptHandle) == sizeof(js::mjit::JITScript *));
+    JS_STATIC_ASSERT(JS_ALIGNMENT_OF(JSScript::JITScriptHandle) ==
+                     JS_ALIGNMENT_OF(js::mjit::JITScript *));
+    JS_STATIC_ASSERT(offsetof(JSScript::JITScriptHandle, value) == 0);
+}
+
 size_t
 JSScript::sizeOfJitScripts(JSMallocSizeOfFun mallocSizeOf)
 {
     size_t n = 0;
-    if (jitNormal)
-        n += jitNormal->sizeOfIncludingThis(mallocSizeOf); 
-    if (jitCtor)
-        n += jitCtor->sizeOfIncludingThis(mallocSizeOf); 
+    if (jitHandleNormal.isValid())
+        n += jitHandleNormal.getValid()->sizeOfIncludingThis(mallocSizeOf); 
+    if (jitHandleCtor.isValid())
+        n += jitHandleCtor.getValid()->sizeOfIncludingThis(mallocSizeOf); 
     return n;
 }
 
 size_t
 mjit::JITScript::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf)
 {
     size_t n = mallocSizeOf(this);
     for (unsigned i = 0; i < nchunks; i++) {
@@ -1433,31 +1441,26 @@ mjit::JITChunk::computedSizeOfIncludingT
 /* Please keep in sync with Compiler::finishThisUp! */
 size_t
 mjit::JITChunk::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf)
 {
     return mallocSizeOf(this);
 }
 
 void
-mjit::ReleaseScriptCode(FreeOp *fop, JSScript *script, bool construct)
+JSScript::ReleaseCode(FreeOp *fop, JITScriptHandle *jith)
 {
     // NB: The recompiler may call ReleaseScriptCode, in which case it
     // will get called again when the script is destroyed, so we
     // must protect against calling ReleaseScriptCode twice.
 
-    JITScript **pjit = construct ? &script->jitCtor : &script->jitNormal;
-    void **parity = construct ? &script->jitArityCheckCtor : &script->jitArityCheckNormal;
-
-    if (*pjit) {
-        (*pjit)->destroy(fop);
-        fop->free_(*pjit);
-        *pjit = NULL;
-        *parity = NULL;
-    }
+    JITScript *jit = jith->getValid();
+    jit->destroy(fop);
+    fop->free_(jit);
+    jith->setEmpty();
 }
 
 #ifdef JS_METHODJIT_PROFILE_STUBS
 void JS_FASTCALL
 mjit::ProfileStubCall(VMFrame &f)
 {
     JSOp op = JSOp(*f.regs.pc);
     StubCallsForOp[op]++;
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -901,26 +901,23 @@ enum CompileRequest
     CompileRequest_Interpreter,
     CompileRequest_JIT
 };
 
 CompileStatus
 CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
              bool construct, CompileRequest request);
 
-void
-ReleaseScriptCode(FreeOp *fop, JSScript *script, bool construct);
-
 inline void
 ReleaseScriptCode(FreeOp *fop, JSScript *script)
 {
-    if (script->jitCtor)
-        mjit::ReleaseScriptCode(fop, script, true);
-    if (script->jitNormal)
-        mjit::ReleaseScriptCode(fop, script, false);
+    if (script->jitHandleCtor.isValid())
+        JSScript::ReleaseCode(fop, &script->jitHandleCtor);
+    if (script->jitHandleNormal.isValid())
+        JSScript::ReleaseCode(fop, &script->jitHandleNormal);
 }
 
 // Expand all stack frames inlined by the JIT within a compartment.
 void
 ExpandInlineFrames(JSCompartment *compartment);
 
 // Return all VMFrames in a compartment to the interpreter. This must be
 // followed by destroying all JIT code in the compartment.
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -550,16 +550,19 @@ mjit::NativeStubEpilogue(VMFrame &f, Ass
  *   3) args == nargs, and the inline call site was patched already.
  *      A small stub is created which extends the original guard to also
  *      guard on the JSFunction lying underneath the function object.
  *
  * If the OOL path does not have a scripted function, but does have a
  * scripted native, then a small stub is generated which inlines the native
  * invocation.
  */
+namespace js {
+namespace mjit {
+
 class CallCompiler : public BaseCompiler
 {
     VMFrame &f;
     CallICInfo &ic;
     bool callingNew;
 
   public:
     CallCompiler(VMFrame &f, CallICInfo &ic, bool callingNew)
@@ -602,26 +605,30 @@ class CallCompiler : public BaseCompiler
         /* Generate the inline frame creation. */
         void *ncode = ic.funGuard.labelAtOffset(ic.joinPointOffset).executableAddress();
         inlFrame.assemble(ncode, f.pc());
 
         /* funObjReg is still valid. Check if a compilation is needed. */
         Address scriptAddr(ic.funObjReg, JSFunction::offsetOfNativeOrScript());
         masm.loadPtr(scriptAddr, t0);
 
-        /*
-         * Test if script->nmap is NULL - same as checking ncode, but faster
-         * here since ncode has two failure modes and we need to load out of
-         * nmap anyway.
-         */
-        size_t offset = callingNew
-                        ? offsetof(JSScript, jitArityCheckCtor)
-                        : offsetof(JSScript, jitArityCheckNormal);
+        // Test that:
+        // - script->jitHandle{Ctor,Normal}->value is neither NULL nor UNJITTABLE, and
+        // - script->jitHandle{Ctor,Normal}->value->arityCheckEntry is not NULL.
+        //
+        size_t offset = JSScript::jitHandleOffset(callingNew);
         masm.loadPtr(Address(t0, offset), t0);
-        Jump hasCode = masm.branchPtr(Assembler::Above, t0, ImmPtr(JS_UNJITTABLE_SCRIPT));
+        Jump hasNoJitCode = masm.branchPtr(Assembler::BelowOrEqual, t0,
+                                           ImmPtr(JSScript::JITScriptHandle::UNJITTABLE));
+
+        masm.loadPtr(Address(t0, offsetof(JITScript, arityCheckEntry)), t0);
+
+        Jump hasCode = masm.branchPtr(Assembler::NotEqual, t0, ImmPtr(0));
+
+        hasNoJitCode.linkTo(masm.label(), &masm);
 
         /*
          * Write the rejoin state to indicate this is a compilation call made
          * from an IC (the recompiler cannot detect calls made from ICs
          * automatically).
          */
         masm.storePtr(ImmPtr((void *) ic.frameSize.rejoinState(f.pc(), false)),
                       FrameAddress(offsetof(VMFrame, stubRejoin)));
@@ -1005,16 +1012,19 @@ class CallCompiler : public BaseCompiler
                     THROWV(NULL);
             }
         }
 
         return ucr.codeAddr;
     }
 };
 
+} // namespace mjit
+} // namespace js
+
 void * JS_FASTCALL
 ic::Call(VMFrame &f, CallICInfo *ic)
 {
     CallCompiler cc(f, *ic, false);
     return cc.update();
 }
 
 void * JS_FASTCALL
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -190,23 +190,23 @@ Recompiler::patchFrame(JSCompartment *co
     } else if (rejoin) {
         /* Recompilation triggered by CompileFunction. */
         if (fp->script() == script) {
             fp->setRejoin(StubRejoin(rejoin));
             *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
             f->stubRejoin = 0;
         }
     } else {
-        if (script->jitCtor) {
-            JITChunk *chunk = script->jitCtor->findCodeChunk(*addr);
+        if (script->jitHandleCtor.isValid()) {
+            JITChunk *chunk = script->jitHandleCtor.getValid()->findCodeChunk(*addr);
             if (chunk)
                 patchCall(chunk, fp, addr);
         }
-        if (script->jitNormal) {
-            JITChunk *chunk = script->jitNormal->findCodeChunk(*addr);
+        if (script->jitHandleNormal.isValid()) {
+            JITChunk *chunk = script->jitHandleNormal.getValid()->findCodeChunk(*addr);
             if (chunk)
                 patchCall(chunk, fp, addr);
         }
     }
 }
 
 StackFrame *
 Recompiler::expandInlineFrameChain(StackFrame *outer, InlineFrame *inner)