[INFER] Rejoin into the interpreter from js_InternalThrow, bug 672123.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 21 Jul 2011 23:03:10 -0700
changeset 76056 85b77c0781b61ca17bd9be12f3ec36d63b9cc507
parent 76055 ed0911cf98f22a79e67d83843e0de6cf8c48aa01
child 76057 5120ea3deef5da64dfdae06fd4332402d20fbaae
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
bugs672123
milestone8.0a1
[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)
 {