[INFER] Always allow rejoins from the interpreter to JM code at loop heads, bug 636598.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 09 Mar 2011 14:07:51 -0800
changeset 74747 8b03f06987426c0284464c906233de4e416a14f3
parent 74746 12eb2698dfdbd9a2480fa201c8e3f3f7e2ca9f78
child 74748 e348689923e520ce8b2ac2a49e618326dcf13caa
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs636598
milestone2.0b13pre
[INFER] Always allow rejoins from the interpreter to JM code at loop heads, bug 636598.
js/src/jsinterp.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/MethodJIT.cpp
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2416,28 +2416,23 @@ Interpret(JSContext *cx, JSStackFrame *e
 
 #define MONITOR_BRANCH_METHODJIT()                                            \
     JS_BEGIN_MACRO                                                            \
         mjit::CompileStatus status =                                          \
             mjit::CanMethodJITAtBranch(cx, script, regs.fp, regs.pc);         \
         if (status == mjit::Compile_Error)                                    \
             goto error;                                                       \
         if (status == mjit::Compile_Okay) {                                   \
-            /* :FIXME: bug 636598 there should always be an nmap for this PC. */   \
-            void *ncode =                                                          \
-                script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc);  \
-            if (ncode) {                                                      \
-                interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode);      \
-                if (inlineCallCount)                                          \
-                    goto jit_return;                                          \
-                regs.fp->setFinishedInInterpreter();                          \
-                goto leave_on_safe_point;                                     \
-            } else {                                                          \
-                useMethodJIT = false;                                         \
-            }                                                                 \
+            void *ncode =                                                     \
+                script->nativeCodeForPC(regs.fp->isConstructing(), regs.pc);  \
+            interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode);          \
+            if (inlineCallCount)                                              \
+                goto jit_return;                                              \
+            regs.fp->setFinishedInInterpreter();                              \
+            goto leave_on_safe_point;                                         \
         }                                                                     \
         if (status == mjit::Compile_Abort) {                                  \
             useMethodJIT = false;                                             \
         }                                                                     \
     JS_END_MACRO
 
 #else
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -108,16 +108,17 @@ mjit::Compiler::Compiler(JSContext *cx, 
     getElemICs(CompilerAllocPolicy(cx, *thisFromCtor())),
     setElemICs(CompilerAllocPolicy(cx, *thisFromCtor())),
 #endif
     callPatches(CompilerAllocPolicy(cx, *thisFromCtor())),
     callSites(CompilerAllocPolicy(cx, *thisFromCtor())), 
     doubleList(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTables(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTableOffsets(CompilerAllocPolicy(cx, *thisFromCtor())),
+    loopEntries(CompilerAllocPolicy(cx, *thisFromCtor())),
     stubcc(cx, *thisFromCtor(), frame, script),
     debugMode_(cx->compartment->debugMode),
 #if defined JS_TRACER
     addTraceHints(cx->traceJitEnabled),
 #else
     addTraceHints(false),
 #endif
     recompiling(false),
@@ -607,20 +608,20 @@ mjit::Compiler::finishThisUp(JITScript *
     JS_ASSERT(execPool);
     JSC::ExecutableAllocator::makeWritable(result, totalSize);
     masm.executableCopy(result);
     stubcc.masm.executableCopy(result + masm.size());
     
     JSC::LinkBuffer fullCode(result, totalSize);
     JSC::LinkBuffer stubCode(result + masm.size(), stubcc.size());
 
-    size_t nNmapLive = 0;
+    size_t nNmapLive = loopEntries.length();
     for (size_t i = 0; i < script->length; i++) {
         analyze::Bytecode *opinfo = analysis->maybeCode(i);
-        if (opinfo && opinfo->safePoint)
+        if (opinfo && opinfo->safePoint && !liveness.getCode(i).loopBackedge)
             nNmapLive++;
     }
 
     /* Please keep in sync with JITScript::scriptDataSize! */
     size_t totalBytes = sizeof(JITScript) +
                         sizeof(NativeMapEntry) * nNmapLive +
 #if defined JS_MONOIC
                         sizeof(ic::GetGlobalNameIC) * getGlobalNames.length() +
@@ -670,16 +671,30 @@ mjit::Compiler::finishThisUp(JITScript *
             if (opinfo && opinfo->safePoint) {
                 Label L = jumpMap[i];
                 JS_ASSERT(L.isValid());
                 jitNmap[ix].bcOff = i;
                 jitNmap[ix].ncode = (uint8 *)(result + masm.distanceOf(L));
                 ix++;
             }
         }
+        for (size_t i = 0; i < loopEntries.length(); i++) {
+            /* Insert the entry at the right position. */
+            const LoopEntry &entry = loopEntries[i];
+            size_t j;
+            for (j = 0; j < ix; j++) {
+                if (jitNmap[j].bcOff > entry.pcOffset) {
+                    memmove(jitNmap + j + 1, jitNmap + j, (ix - j) * sizeof(NativeMapEntry));
+                    break;
+                }
+            }
+            jitNmap[j].bcOff = entry.pcOffset;
+            jitNmap[j].ncode = (uint8 *) stubCode.locationOf(entry.label).executableAddress();
+            ix++;
+        }
     }
     JS_ASSERT(ix == jit->nNmapPairs);
 
 #if defined JS_MONOIC
     JS_INIT_CLIST(&jit->callers);
 
     ic::GetGlobalNameIC *getGlobalNames_ = (ic::GetGlobalNameIC *)cursor;
     jit->nGetGlobalNames = getGlobalNames.length();
@@ -5127,16 +5142,31 @@ mjit::Compiler::finishLoop(jsbytecode *h
     jsbytecode *entryTarget;
     frame.popLoop(head, &entry, &entryTarget);
 
     if (!jumpInScript(entry, entryTarget))
         return false;
 
     fallthrough.linkTo(masm.label(), &masm);
 
+    if (!analysis->getCode(head).safePoint) {
+        /*
+         * Emit a stub into the OOL path which loads registers from a synced state
+         * and jumps to the loop head, for rejoining from the interpreter.
+         */
+        LoopEntry entry;
+        entry.pcOffset = head - script->code;
+        entry.label = stubcc.masm.label();
+        loopEntries.append(entry);
+
+        frame.prepareForJump(head, stubcc.masm, true);
+        if (!stubcc.jumpInScript(stubcc.masm.jump(), head))
+            return false;
+    }
+
     return true;
 }
 
 /*
  * Note: This function emits tracer hooks into the OOL path. This means if
  * it is used in the middle of an in-progress slow path, the stream will be
  * hopelessly corrupted. Take care to only call this before linkExits() and
  * after rejoin()s.
@@ -5202,18 +5232,18 @@ mjit::Compiler::jumpAndTrace(Jump j, jsb
             if (slow) {
                 slow->linkTo(stubcc.masm.label(), &stubcc.masm);
                 frame.prepareForJump(target, stubcc.masm, true);
                 if (!stubcc.jumpInScript(stubcc.masm.jump(), target))
                     return false;
             }
         }
 
-        if (target < PC && !finishLoop(target))
-            return false;
+        if (target < PC)
+            return finishLoop(target);
         return true;
     }
 
     /* The trampoline should not be specified if we need to generate a trace IC. */
     JS_ASSERT(!trampoline);
 
 #ifndef JS_TRACER
     JS_NOT_REACHED("Bad addTraceHints");
@@ -5290,19 +5320,17 @@ mjit::Compiler::jumpAndTrace(Jump j, jsb
      * on backwards jumps.
      */
     frame.prepareForJump(target, stubcc.masm, true);
 
     if (!stubcc.jumpInScript(stubcc.masm.jump(), target))
         return false;
 #endif
 
-    if (!finishLoop(target))
-        return false;
-    return true;
+    return finishLoop(target);
 }
 
 void
 mjit::Compiler::enterBlock(JSObject *obj)
 {
     // If this is an exception entry point, then jsl_InternalThrow has set
     // VMFrame::fp to the correct fp for the entry point. We need to copy
     // that value here to FpReg so that FpReg also has the correct sp.
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -325,16 +325,21 @@ class Compiler : public BaseCompiler
         bool ool;
     };
 
     struct JumpTable {
         DataLabelPtr label;
         size_t offsetIndex;
     };
 
+    struct LoopEntry {
+        uint32 pcOffset;
+        Label label;
+    };
+
     JSStackFrame *fp;
     JSScript *script;
     JSObject *scopeChain;
     JSObject *globalObj;
     JSFunction *fun;
     bool isConstructing;
     analyze::Script *analysis;
     Label *jumpMap;
@@ -356,16 +361,17 @@ class Compiler : public BaseCompiler
     js::Vector<GetElementICInfo, 16, CompilerAllocPolicy> getElemICs;
     js::Vector<SetElementICInfo, 16, CompilerAllocPolicy> setElemICs;
 #endif
     js::Vector<CallPatchInfo, 64, CompilerAllocPolicy> callPatches;
     js::Vector<InternalCallSite, 64, CompilerAllocPolicy> callSites;
     js::Vector<DoublePatch, 16, CompilerAllocPolicy> doubleList;
     js::Vector<JumpTable, 16> jumpTables;
     js::Vector<uint32, 16> jumpTableOffsets;
+    js::Vector<LoopEntry, 16> loopEntries;
     StubCompiler stubcc;
     Label invokeLabel;
     Label arityLabel;
     bool debugMode_;
     bool addTraceHints;
     bool recompiling;
     bool hasThisType;
     JSValueType thisType;
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -805,16 +805,45 @@ mjit::JaegerShot(JSContext *cx)
 
 JSBool
 js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint)
 {
 #ifdef JS_TRACER
     JS_ASSERT(!TRACE_RECORDER(cx));
 #endif
 
+    JSScript *script = cx->fp()->script();
+    if (cx->typeInferenceEnabled() && script->varTypes) {
+        /*
+         * Convert integer locals/args to doubles as required. The code we are
+         * jumping to may assume that non-escaping locals and args have double
+         * values if they were inferred as 'int or double'. The interpreter cannot
+         * guarantee this holds, so we check and fixup the args/locals here.
+         */
+
+        if (cx->fp()->hasArgs()) {
+            JSFunction *fun = cx->fp()->fun();
+            Value *formals = cx->fp()->formalArgs();
+            for (uint32 i = 0; i < fun->nargs; i++) {
+                if (formals[i].isInt32() &&
+                    script->argTypes(i)->getKnownTypeTag(cx, NULL) == JSVAL_TYPE_DOUBLE) {
+                    formals[i].setDouble((double)formals[i].toInt32());
+                }
+            }
+        }
+
+        Value *fixed = cx->fp()->slots();
+        for (uint32 i = 0; i < script->nfixed; i++) {
+            if (fixed[i].isInt32() &&
+                script->localTypes(i)->getKnownTypeTag(cx, NULL) == JSVAL_TYPE_DOUBLE) {
+                fixed[i].setDouble((double)fixed[i].toInt32());
+            }
+        }
+    }
+
     return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint);
 }
 
 NativeMapEntry *
 JITScript::nmap() const
 {
     return (NativeMapEntry *)((char*)this + sizeof(JITScript));
 }