[INFER] Rejoin into the interpreter from js_InternalThrow, bug 672123.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 21 Jul 2011 23:03:10 -0700
changeset 77367 85b77c0781b61ca17bd9be12f3ec36d63b9cc507
parent 77366 ed0911cf98f22a79e67d83843e0de6cf8c48aa01
child 77368 5120ea3deef5da64dfdae06fd4332402d20fbaae
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs672123
milestone8.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
[INFER] Rejoin into the interpreter from js_InternalThrow, bug 672123.
js/src/jit-test/tests/jaeger/recompile/bug672123.js
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/Retcon.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/recompile/bug672123.js
@@ -0,0 +1,46 @@
+var caught = false;
+function h(code) {
+    f = eval("(function(){" + code + "})")
+    g()
+}
+function g() {
+    try {
+      f();
+    } catch (r) { caught = true }
+}
+h("")
+for (i = 0; i < 9; i++) {
+    h("")
+}
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("")
+h("\"\"(gc())")
+assertEq(caught, true);
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -579,26 +579,59 @@ js_InternalThrow(VMFrame &f)
     JS_ASSERT(f.regs.sp == cx->regs().sp);
 
     if (!pc)
         return NULL;
 
     StackFrame *fp = cx->fp();
     JSScript *script = fp->script();
 
-    if (!fp->jit()) {
+    if (cx->typeInferenceEnabled()) {
         /*
-         * This frame had JIT code at one point, but it has since been
-         * discarded due to a recompilation. Recompile it now. This can only
-         * fail due to OOM, in which case that OOM will propagate above the
-         * JaegerShot activation.
+         * Fall back to EnterMethodJIT and finish the frame in the interpreter.
+         * With type inference enabled, we may wipe out all JIT code on the
+         * stack without patching ncode values to jump to the interpreter, and
+         * thus can only enter JIT code via EnterMethodJIT (which overwrites
+         * its entry frame's ncode). See ClearAllFrames.
          */
-        CompileStatus status = TryCompile(cx, fp);
-        if (status != Compile_Okay)
+        cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished);
+
+        analyze::AutoEnterAnalysis enter(cx);
+        analyze::ScriptAnalysis *analysis = script->analysis(cx);
+        if (analysis && !analysis->ranBytecode())
+            analysis->analyzeBytecode(cx);
+        if (!analysis || analysis->OOM()) {
+            js_ReportOutOfMemory(cx);
             return NULL;
+        }
+
+        cx->regs().pc = pc;
+        cx->regs().sp = fp->base() + analysis->getCode(pc).stackDepth;
+
+        /*
+         * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go
+         * back into the interpreter with a pending exception. This will cause
+         * it to immediately rethrow.
+         */
+        if (cx->isExceptionPending()) {
+            JS_ASSERT(js_GetOpcode(cx, script, pc) == JSOP_ENTERBLOCK);
+            JSObject *obj = script->getObject(GET_SLOTNO(pc));
+            Value *vp = cx->regs().sp + OBJ_BLOCK_COUNT(cx, obj);
+            SetValueRangeToUndefined(cx->regs().sp, vp);
+            cx->regs().sp = vp;
+            JS_ASSERT(js_GetOpcode(cx, script, pc + JSOP_ENTERBLOCK_LENGTH) == JSOP_EXCEPTION);
+            cx->regs().sp[0] = cx->getPendingException();
+            cx->clearPendingException();
+            cx->regs().sp++;
+            cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH;
+        }
+
+        *f.oldregs = f.regs;
+
+        return NULL;
     }
 
     return script->nativeCodeForPC(fp->isConstructing(), pc);
 }
 
 void JS_FASTCALL
 stubs::CreateFunCallObject(VMFrame &f)
 {
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -324,25 +324,30 @@ struct RecompilationMonitor
     /*
      * If either inline frame expansion or recompilation occurs, then ICs and
      * stubs should not depend on the frame or JITs being intact. The two are
      * separated for logging.
      */
     unsigned recompilations;
     unsigned frameExpansions;
 
+    /* If a GC occurs it may discard jit code on the stack. */
+    unsigned gcNumber;
+
     RecompilationMonitor(JSContext *cx)
         : cx(cx),
           recompilations(cx->compartment->types.recompilations),
-          frameExpansions(cx->compartment->types.frameExpansions)
+          frameExpansions(cx->compartment->types.frameExpansions),
+          gcNumber(cx->runtime->gcNumber)
     {}
 
     bool recompiled() {
         return cx->compartment->types.recompilations != recompilations
-            || cx->compartment->types.frameExpansions != frameExpansions;
+            || cx->compartment->types.frameExpansions != frameExpansions
+            || cx->runtime->gcNumber != gcNumber;
     }
 };
 
 /*
  * Trampolines to force returns from jit code.
  * See also TrampolineCompiler::generateForceReturn(Fast).
  */
 struct Trampolines {
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -378,20 +378,22 @@ ClearAllFrames(JSCompartment *compartmen
         return;
 
     ExpandInlineFrames(compartment, true);
 
     for (VMFrame *f = compartment->jaegerCompartment()->activeFrame();
          f != NULL;
          f = f->previous) {
 
-        // We don't need to scan the frames internal to this VMFrame.
-        // Patching the VMFrame's return address will cause all its frames to
-        // finish in the interpreter (unless the interpreter enters one of the
-        // intermediate frames at a loop boundary).
+        // We don't need to scan the frames internal to this VMFrame, or update
+        // their ncode values. Patching the VMFrame's return address will cause
+        // all its frames to finish in the interpreter, unless the interpreter
+        // enters one of the intermediate frames at a loop boundary. In such a
+        // case, the interpreter will enter through EnterMethodJIT, which
+        // overwrites the entry frame's ncode value.
 
         Recompiler::patchFrame(compartment, f, f->fp()->script());
     }
 }
 
 Recompiler::Recompiler(JSContext *cx, JSScript *script)
   : cx(cx), script(script)
 {