Move JIT handles in scripts to a separate structure, bug 758613. r=dvander
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 25 May 2012 08:20:33 -0700
changeset 94959 de141e924806c285368700c3ba977402fdc5a837
parent 94958 1585459db40a05b9d79db5de28a0f7052d4f96cb
child 94960 49859cdadbc44eb99f292c375a5220e5f78ec6ed
push id9886
push userbhackett@mozilla.com
push dateFri, 25 May 2012 15:20:44 +0000
treeherdermozilla-inbound@de141e924806 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs758613
milestone15.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
Move JIT handles in scripts to a separate structure, bug 758613. r=dvander
js/src/jsinfer.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.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
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2125,18 +2125,17 @@ TypeCompartment::nukeTypes(FreeOp *fop)
     JSCompartment *compartment = this->compartment();
     mjit::ExpandInlineFrames(compartment);
     mjit::ClearAllFrames(compartment);
 
     /* Throw away all JIT code in the compartment, but leave everything else alone. */
 
     for (gc::CellIter i(compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
-        if (script->hasJITCode())
-            mjit::ReleaseScriptCode(fop, script);
+        mjit::ReleaseScriptCode(fop, script);
     }
 #endif /* JS_METHODJIT */
 
 }
 
 void
 TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
 {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1937,17 +1937,17 @@ JSScript::ensureHasDebugScript(JSContext
 
     return true;
 }
 
 void
 JSScript::recompileForStepMode(FreeOp *fop)
 {
 #ifdef JS_METHODJIT
-    if (hasJITCode()) {
+    if (hasJITInfo()) {
         mjit::Recompiler::clearStackReferences(fop, this);
         mjit::ReleaseScriptCode(fop, this);
     }
 #endif
 }
 
 bool
 JSScript::tryNewStepMode(JSContext *cx, uint32_t newValue)
@@ -2201,17 +2201,17 @@ JSScript::applySpeculationFailed(JSConte
                 /* Note: 'arguments' may have already been overwritten. */
                 if (fp->localSlot(slot).isMagic(JS_OPTIMIZED_ARGUMENTS))
                     fp->localSlot(slot) = ObjectValue(*obj);
             }
         }
     }
 
 #ifdef JS_METHODJIT
-    if (script->hasJITCode()) {
+    if (script->hasJITInfo()) {
         mjit::Recompiler::clearStackReferences(cx->runtime->defaultFreeOp(), script);
         mjit::ReleaseScriptCode(cx->runtime->defaultFreeOp(), script);
     }
 #endif
 
     if (script->hasAnalysis() && script->analysis()->ranInference()) {
         types::AutoEnterTypeInference enter(cx);
         types::TypeScript::MonitorUnknown(cx, script, script->argumentsBytecode());
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -374,16 +374,36 @@ struct JSScript : public js::gc::Cell
         void setUnjittable()    { value = const_cast<js::mjit::JITScript *>(UNJITTABLE); }
         void setValid(js::mjit::JITScript *jit) {
             value = jit;
             JS_ASSERT(isValid());
         }
 
         static void staticAsserts();
     };
+
+    // All the possible JITScripts that can simultaneously exist for a script.
+    struct JITScriptSet
+    {
+        JITScriptHandle jitHandleNormal;          // JIT info for normal scripts
+        JITScriptHandle jitHandleNormalBarriered; // barriered JIT info for normal scripts
+        JITScriptHandle jitHandleCtor;            // JIT info for constructors
+        JITScriptHandle jitHandleCtorBarriered;   // barriered JIT info for constructors
+
+        static size_t jitHandleOffset(bool constructing, bool barriers) {
+            return constructing
+                ? (barriers
+                   ? offsetof(JITScriptSet, jitHandleCtorBarriered)
+                   : offsetof(JITScriptSet, jitHandleCtor))
+                : (barriers
+                   ? offsetof(JITScriptSet, jitHandleNormalBarriered)
+                   : offsetof(JITScriptSet, jitHandleNormal));
+        }
+    };
+
 #endif  // JS_METHODJIT
 
     //
     // We order fields according to their size in order to avoid wasting space
     // for alignment.
     //
 
     // Larger-than-word-sized fields.
@@ -416,25 +436,21 @@ struct JSScript : public js::gc::Cell
      *   scripts the global should be extracted from the JS frame that
      *   execute scripts.
      */
     js::HeapPtr<js::GlobalObject, JSScript*> globalObject;
 
     /* Persistent type information retained across GCs. */
     js::types::TypeScript *types;
 
-  public:
+  private:
 #ifdef JS_METHODJIT
-    JITScriptHandle jitHandleNormal;          // JIT info for normal scripts
-    JITScriptHandle jitHandleNormalBarriered; // barriered JIT info for normal scripts
-    JITScriptHandle jitHandleCtor;            // JIT info for constructors
-    JITScriptHandle jitHandleCtorBarriered;   // barriered JIT info for constructors
+    JITScriptSet *jitInfo;
 #endif
 
-  private:
     js::HeapPtrFunction function_;
 
     // 32-bit fields.
 
   public:
     uint32_t        length;     /* length of code vector */
 
     uint32_t        lineno;     /* base line number of script */
@@ -444,17 +460,17 @@ struct JSScript : public js::gc::Cell
 
     uint32_t        natoms;     /* length of atoms array */
 
   private:
     uint32_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 !defined(JS_METHODJIT) && JS_BITS_PER_WORD == 32
+#if JS_BITS_PER_WORD == 32
     uint32_t        pad32;
 #endif
 
 #ifdef DEBUG
     // Unique identifier within the compartment for this script, used for
     // printing analysis information.
     uint32_t        id_;
   private:
@@ -661,37 +677,36 @@ struct JSScript : public js::gc::Cell
     bool makeAnalysis(JSContext *cx);
 
 #ifdef JS_METHODJIT
   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, bool barriers) {
-        return constructing
-            ? (barriers ? offsetof(JSScript, jitHandleCtorBarriered) : offsetof(JSScript, jitHandleCtor))
-            : (barriers ? offsetof(JSScript, jitHandleNormalBarriered) : offsetof(JSScript, jitHandleNormal));
+  public:
+    bool hasJITInfo() {
+        return jitInfo != NULL;
     }
 
-  public:
-    bool hasJITCode() {
-        return jitHandleNormal.isValid()
-            || jitHandleNormalBarriered.isValid()
-            || jitHandleCtor.isValid()
-            || jitHandleCtorBarriered.isValid();
-    }
+    static size_t offsetOfJITInfo() { return offsetof(JSScript, jitInfo); }
+
+    inline bool ensureHasJITInfo(JSContext *cx);
+    inline void destroyJITInfo(js::FreeOp *fop);
 
     JITScriptHandle *jitHandle(bool constructing, bool barriers) {
+        JS_ASSERT(jitInfo);
         return constructing
-               ? (barriers ? &jitHandleCtorBarriered : &jitHandleCtor)
-               : (barriers ? &jitHandleNormalBarriered : &jitHandleNormal);
+               ? (barriers ? &jitInfo->jitHandleCtorBarriered : &jitInfo->jitHandleCtor)
+               : (barriers ? &jitInfo->jitHandleNormalBarriered : &jitInfo->jitHandleNormal);
     }
 
     js::mjit::JITScript *getJIT(bool constructing, bool barriers) {
+        if (!jitInfo)
+            return NULL;
         JITScriptHandle *jith = jitHandle(constructing, barriers);
         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);
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -208,16 +208,34 @@ JSScript::clearNesting()
 {
     js::types::TypeScriptNesting *nesting = this->nesting();
     if (nesting) {
         js::Foreground::delete_(nesting);
         types->nesting = NULL;
     }
 }
 
+#ifdef JS_METHODJIT
+inline bool
+JSScript::ensureHasJITInfo(JSContext *cx)
+{
+    if (jitInfo)
+        return true;
+    jitInfo = cx->new_<JITScriptSet>();
+    return jitInfo != NULL;
+}
+
+inline void
+JSScript::destroyJITInfo(js::FreeOp *fop)
+{
+    fop->delete_(jitInfo);
+    jitInfo = NULL;
+}
+#endif /* JS_METHODJIT */
+
 inline void
 JSScript::writeBarrierPre(JSScript *script)
 {
 #ifdef JSGC_INCREMENTAL
     if (!script)
         return;
 
     JSCompartment *comp = script->compartment();
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -106,16 +106,18 @@ mjit::Compiler::Compiler(JSContext *cx, 
 
 CompileStatus
 mjit::Compiler::compile()
 {
     JS_ASSERT(!outerChunkRef().chunk);
 
     CompileStatus status = performCompilation();
     if (status != Compile_Okay && status != Compile_Retry) {
+        if (!outerScript->ensureHasJITInfo(cx))
+            return Compile_Error;
         JSScript::JITScriptHandle *jith = outerScript->jitHandle(isConstructing, cx->compartment->needsBarrier());
         JSScript::ReleaseCode(cx->runtime->defaultFreeOp(), jith);
         jith->setUnjittable();
 
         if (outerScript->function()) {
             outerScript->uninlineable = true;
             types::MarkTypeObjectFlags(cx, outerScript->function(),
                                        types::OBJECT_FLAG_UNINLINEABLE);
@@ -904,19 +906,21 @@ MakeJITScript(JSContext *cx, JSScript *s
 CompileStatus
 mjit::CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
                    bool construct, CompileRequest request)
 {
   restart:
     if (!cx->methodJitEnabled)
         return Compile_Abort;
 
-    JSScript::JITScriptHandle *jith = script->jitHandle(construct, cx->compartment->needsBarrier());
-    if (jith->isUnjittable())
-        return Compile_Abort;
+    if (script->hasJITInfo()) {
+        JSScript::JITScriptHandle *jith = script->jitHandle(construct, cx->compartment->needsBarrier());
+        if (jith->isUnjittable())
+            return Compile_Abort;
+    }
 
     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;
@@ -926,16 +930,21 @@ mjit::CanMethodJIT(JSContext *cx, JSScri
         return Compile_Error;
 
     // Ensure that constructors have at least one slot.
     if (construct && !script->nslots)
         script->nslots++;
 
     uint64_t gcNumber = cx->runtime->gcNumber;
 
+    if (!script->ensureHasJITInfo(cx))
+        return Compile_Error;
+
+    JSScript::JITScriptHandle *jith = script->jitHandle(construct, cx->compartment->needsBarrier());
+
     JITScript *jit;
     if (jith->isEmpty()) {
         jit = MakeJITScript(cx, script);
         if (!jit)
             return Compile_Error;
         jith->setValid(jit);
     } else {
         jit = jith->getValid();
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1361,17 +1361,20 @@ JSScript::JITScriptHandle::staticAsserts
     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 (!hasJITInfo())
+        return 0;
+
+    size_t n = mallocSizeOf(jitInfo);
     for (int constructing = 0; constructing <= 1; constructing++) {
         for (int barriers = 0; barriers <= 1; barriers++) {
             JITScript *jit = getJIT((bool) constructing, (bool) barriers);
             if (jit)
                 n += jit->sizeOfIncludingThis(mallocSizeOf);
         }
     }
     return n;
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -867,23 +867,28 @@ enum CompileRequest
 
 CompileStatus
 CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
              bool construct, CompileRequest request);
 
 inline void
 ReleaseScriptCode(FreeOp *fop, JSScript *script)
 {
+    if (!script->hasJITInfo())
+        return;
+
     for (int constructing = 0; constructing <= 1; constructing++) {
         for (int barriers = 0; barriers <= 1; barriers++) {
             JSScript::JITScriptHandle *jith = script->jitHandle((bool) constructing, (bool) barriers);
             if (jith && jith->isValid())
                 JSScript::ReleaseCode(fop, jith);
         }
     }
+
+    script->destroyJITInfo(fop);
 }
 
 // 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
@@ -566,28 +566,32 @@ class CallCompiler : public BaseCompiler
         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 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, f.cx->compartment->needsBarrier());
+        // - script->jitInfo is not NULL
+        // - script->jitInfo->jitHandle{Ctor,Normal}->value is neither NULL nor UNJITTABLE, and
+        // - script->jitInfo->jitHandle{Ctor,Normal}->value->arityCheckEntry is not NULL.
+        masm.loadPtr(Address(t0, JSScript::offsetOfJITInfo()), t0);
+        Jump hasNoJitInfo = masm.branchPtr(Assembler::Equal, t0, ImmPtr(NULL));
+        size_t offset = JSScript::JITScriptSet::jitHandleOffset(callingNew,
+                                                                f.cx->compartment->needsBarrier());
         masm.loadPtr(Address(t0, offset), t0);
         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));
 
+        hasNoJitInfo.linkTo(masm.label(), &masm);
         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)),
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -389,17 +389,17 @@ ClearAllFrames(JSCompartment *compartmen
  *
  * - For VMFrames whose entryncode address (the value of entryfp->ncode before
  *   being clobbered with JaegerTrampolineReturn) is in the original script,
  *   redirect that entryncode to the interpoline.
  */
 void
 Recompiler::clearStackReferences(FreeOp *fop, JSScript *script)
 {
-    JS_ASSERT(script->hasJITCode());
+    JS_ASSERT(script->hasJITInfo());
 
     JaegerSpew(JSpew_Recompile, "recompiling script (file \"%s\") (line \"%d\") (length \"%d\")\n",
                script->filename, script->lineno, script->length);
 
     JSCompartment *comp = script->compartment();
     types::AutoEnterTypeInference enter(fop, comp);
 
     /*
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -226,17 +226,17 @@ BreakpointSite::BreakpointSite(JSScript 
     JS_ASSERT(!script->hasBreakpointsAt(pc));
     JS_INIT_CLIST(&breakpoints);
 }
 
 void
 BreakpointSite::recompile(FreeOp *fop)
 {
 #ifdef JS_METHODJIT
-    if (script->hasJITCode()) {
+    if (script->hasJITInfo()) {
         mjit::Recompiler::clearStackReferences(fop, script);
         mjit::ReleaseScriptCode(fop, script);
     }
 #endif
 }
 
 void
 BreakpointSite::inc(FreeOp *fop)
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -331,17 +331,17 @@ GlobalObject::clear(JSContext *cx)
 #ifdef JS_METHODJIT
     /*
      * Destroy compiled code for any scripts parented to this global. Call ICs
      * can directly call scripts which have associated JIT code, and do so
      * without checking whether the script's global has been cleared.
      */
     for (gc::CellIter i(cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
-        if (script->compileAndGo && script->hasJITCode() && script->hasClearedGlobal()) {
+        if (script->compileAndGo && script->hasJITInfo() && script->hasClearedGlobal()) {
             mjit::Recompiler::clearStackReferences(cx->runtime->defaultFreeOp(), script);
             mjit::ReleaseScriptCode(cx->runtime->defaultFreeOp(), script);
         }
     }
 #endif
 }
 
 bool