Bug 773655 - Don't destroy JM code before Ion compilation. r=bhackett
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 17 Jul 2012 13:20:28 +0200
changeset 106601 2a4e60c030c594bc766c008c1232513f30f67aba
parent 106600 c967c6b03afa3cc95ff7b101478f8d139d79d0e2
child 106602 699ab277c0b82794a6e19090431623c8f2376354
push id14706
push usereakhgari@mozilla.com
push dateTue, 11 Sep 2012 20:39:52 +0000
treeherdermozilla-inbound@d50bf1edaabe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs773655
milestone16.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 773655 - Don't destroy JM code before Ion compilation. r=bhackett
js/src/methodjit/Compiler.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/StubCalls.cpp
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1003,16 +1003,29 @@ mjit::CanMethodJIT(JSContext *cx, JSScri
         jith->setValid(jit);
     } else {
         jit = jith->getValid();
     }
 
     unsigned chunkIndex = jit->chunkIndex(pc);
     ChunkDescriptor &desc = jit->chunkDescriptor(chunkIndex);
 
+    if (jit->mustDestroyEntryChunk) {
+        // We kept this chunk around so that Ion can get info from its caches.
+        // If we end up here, we decided not to use Ion so we can destroy the
+        // chunk now.
+        JS_ASSERT(jit->nchunks == 1);
+        jit->mustDestroyEntryChunk = false;
+
+        if (desc.chunk) {
+            jit->destroyChunk(cx->runtime->defaultFreeOp(), chunkIndex, /* resetUses = */ false);
+            return Compile_Skipped;
+        }
+    }
+
     if (desc.chunk)
         return Compile_Okay;
 
     if (!cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
         ++desc.counter <= INFER_USES_BEFORE_COMPILE)
     {
         return Compile_Skipped;
     }
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1301,33 +1301,17 @@ JITScript::destroyChunk(FreeOp *fop, uns
         desc.counter = 0;
 
     if (chunkIndex == 0) {
         if (argsCheckPool) {
             argsCheckPool->release();
             argsCheckPool = NULL;
         }
 
-        invokeEntry = NULL;
-        fastEntry = NULL;
-        argsCheckEntry = NULL;
-        arityCheckEntry = 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();
-        }
+        disableScriptEntry();
     }
 }
 
 void
 JITScript::trace(JSTracer *trc)
 {
     for (unsigned i = 0; i < nchunks; i++) {
         ChunkDescriptor &desc = chunkDescriptor(i);
@@ -1341,16 +1325,38 @@ JITScript::purgeCaches()
 {
     for (unsigned i = 0; i < nchunks; i++) {
         ChunkDescriptor &desc = chunkDescriptor(i);
         if (desc.chunk)
             desc.chunk->purgeCaches();
     }
 }
 
+void
+JITScript::disableScriptEntry()
+{
+    invokeEntry = NULL;
+    fastEntry = NULL;
+    argsCheckEntry = NULL;
+    arityCheckEntry = NULL;
+
+    // Fixup any ICs still referring to this script.
+    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 *));
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -774,16 +774,23 @@ struct JITScript
     uint32_t        nedges;
 
     /*
      * Pool for shims which transfer control to the interpreter on cross chunk
      * edges to chunks which do not have compiled code.
      */
     JSC::ExecutablePool *shimPool;
 
+    /*
+     * If set, we decided to keep the JITChunk so that Ion can access its caches.
+     * The chunk has to be destroyed the next time the script runs in JM.
+     * Note that this flag implies nchunks == 1.
+     */
+    bool mustDestroyEntryChunk;
+
 #ifdef JS_MONOIC
     /* Inline cache at function entry for checking this/argument types. */
     JSC::CodeLocationLabel argsCheckStub;
     JSC::CodeLocationLabel argsCheckFallthrough;
     JSC::CodeLocationJump  argsCheckJump;
     JSC::ExecutablePool *argsCheckPool;
     void resetArgsCheck();
 #endif
@@ -824,16 +831,18 @@ struct JITScript
 
     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf);
 
     void destroy(FreeOp *fop);
     void destroyChunk(FreeOp *fop, unsigned chunkIndex, bool resetUses = true);
 
     void trace(JSTracer *trc);
     void purgeCaches();
+
+    void disableScriptEntry();
 };
 
 /*
  * Execute the given mjit code. This is a low-level call and callers must
  * provide the same guarantees as JaegerShot/CheckStackAndEnterMethodJIT.
  */
 JaegerStatus EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit,
                             bool partial);
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -35,16 +35,20 @@
 #include "jsatominlines.h"
 #include "StubCalls-inl.h"
 #include "jsfuninlines.h"
 #include "jstypedarray.h"
 
 #include "vm/RegExpObject-inl.h"
 #include "vm/String-inl.h"
 
+#ifdef JS_ION
+#include "ion/Ion.h"
+#endif
+
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 
 #include "jsautooplen.h"
 
 using namespace js;
 using namespace js::mjit;
@@ -775,18 +779,35 @@ stubs::Interrupt(VMFrame &f, jsbytecode 
 
     if (!js_HandleExecutionInterrupt(f.cx))
         THROW();
 }
 
 void JS_FASTCALL
 stubs::RecompileForInline(VMFrame &f)
 {
+    JSScript *script = f.script();
+
     ExpandInlineFrames(f.cx->compartment);
-    Recompiler::clearStackReferences(f.cx->runtime->defaultFreeOp(), f.script());
+    Recompiler::clearStackReferences(f.cx->runtime->defaultFreeOp(), script);
+
+#ifdef JS_ION
+    if (ion::IsEnabled(f.cx) && f.jit()->nchunks == 1 &&
+        script->canIonCompile() && !script->hasIonScript())
+    {
+        // After returning to the interpreter, IonMonkey will try to compile
+        // this script. Don't destroy the JITChunk immediately so that Ion
+        // still has access to its ICs.
+        JS_ASSERT(!f.jit()->mustDestroyEntryChunk);
+        f.jit()->mustDestroyEntryChunk = true;
+        f.jit()->disableScriptEntry();
+        return;
+    }
+#endif
+
     f.jit()->destroyChunk(f.cx->runtime->defaultFreeOp(), f.chunkIndex(), /* resetUses = */ false);
 }
 
 void JS_FASTCALL
 stubs::Trap(VMFrame &f, uint32_t trapTypes)
 {
     Value rval;