[INFER] Patch unsynced local types when expanding inline frames, bug 648567.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 08 Apr 2011 17:57:58 -0700
changeset 74916 ef890e014ce1d48a0f539ccf765bbaa57a08492e
parent 74915 68124edcaa4f8bf430240cfdc9d174cb933f0c36
child 74917 a4355f02771640641de322b8bf0e0a24f428f974
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs648567
milestone2.2a1pre
[INFER] Patch unsynced local types when expanding inline frames, bug 648567.
js/src/jit-test/tests/jaeger/recompile/bug648567.js
js/src/jsanalyze.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/recompile/bug648567.js
@@ -0,0 +1,16 @@
+var arr = [-10, true];
+true || arr[0];
+
+function g() {
+    var x = arr[12];
+    var y = arr.length;
+    arr[undefined] = x;
+    assertEq(y, 2);
+}
+{
+    function f() {
+        gc();
+        g();        
+    }
+    f();
+}
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -306,17 +306,17 @@ Script::analyze(JSContext *cx, JSScript 
     /*
      * If the script is in debug mode, JS_SetFrameReturnValue can be called at
      * any safe point.
      */
     if (cx->compartment->debugMode)
         usesRval = true;
 
     isInlineable = true;
-    if (script->nClosedArgs || script->nClosedVars ||
+    if (script->nClosedArgs || script->nClosedVars || script->nfixed >= LOCAL_LIMIT ||
         (script->fun && script->fun->isHeavyweight()) ||
         script->usesEval || script->usesArguments || cx->compartment->debugMode) {
         isInlineable = false;
     }
 
     /*
      * If we are in the middle of one or more jumps, the offset of the highest
      * target jumping over this bytecode.  Includes implicit jumps from
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -647,16 +647,42 @@ mjit::Compiler::generatePrologue()
         }
     }
 
     recompileCheckHelper();
 
     return Compile_Okay;
 }
 
+void
+mjit::Compiler::generateInlinePrologue()
+{
+    /*
+     * The types of locals in inlined frames are left unsynced even if known.
+     * (Note that we mark as uninlineable all scripts containing locals with
+     * uses before defs). We will treat these locals as synced while compiling
+     * the script, and need to actually do the syncing if we ever expand the
+     * inline frame (which happens on recompilation, and recompilation is the
+     * only way we will ever need to observe the known type of the local).
+     */
+    for (uint32 i = 0; i < script->nfixed; i++) {
+        JS_ASSERT(!a->analysis.localHasUseBeforeDef(i));
+        JSValueType type = knownLocalType(i);
+        if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE) {
+            FrameEntry *fe = frame.getLocal(i);
+            UnsyncedEntry entry;
+            PodZero(&entry);
+            entry.offset = frame.frameOffset(fe);
+            entry.knownType = true;
+            entry.u.type = type;
+            a->unsyncedEntries.append(entry);
+        }
+    }
+}
+
 CompileStatus
 mjit::Compiler::generateEpilogue()
 {
     return Compile_Okay;
 }
 
 CompileStatus
 mjit::Compiler::finishThisUp(JITScript **jitp)
@@ -3902,16 +3928,17 @@ mjit::Compiler::inlineScriptedFunction(u
         a->returnValueDouble = returnType == JSVAL_TYPE_DOUBLE;
         if (returnSet) {
             a->returnSet = true;
             a->returnRegister = returnRegister;
             a->returnParentRegs = returnParentRegs;
         }
         a->temporaryParentRegs = temporaryParentRegs;
 
+        generateInlinePrologue();
         status = generateMethod();
         if (status != Compile_Okay) {
             popActiveFrame();
             if (status == Compile_Abort) {
                 /* The callee is uncompileable, mark it as uninlineable and retry. */
                 if (!cx->markTypeFunctionUninlineable(fun->getType()))
                     return Compile_Error;
                 return Compile_Retry;
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -489,16 +489,17 @@ class Compiler : public BaseCompiler
   private:
     CompileStatus performCompilation(JITScript **jitp);
     CompileStatus generatePrologue();
     CompileStatus generateMethod();
     CompileStatus generateEpilogue();
     CompileStatus finishThisUp(JITScript **jitp);
     CompileStatus pushActiveFrame(JSScript *script, uint32 argc);
     void popActiveFrame();
+    void generateInlinePrologue();
 
     /* Analysis helpers. */
     CompileStatus prepareInferenceTypes(JSScript *script, ActiveFrame *a);
     void fixDoubleTypes(Uses uses);
     void restoreAnalysisTypes(uint32 stackDepth);
     JSValueType knownThisType();
     JSValueType knownArgumentType(uint32 arg);
     JSValueType knownLocalType(uint32 local);
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -84,22 +84,22 @@ FrameState::getUnsyncedEntries(uint32 *p
         if (fe->type.synced() && fe->data.synced())
             continue;
         if (fe->inlined)
             continue;
 
         UnsyncedEntry entry;
         PodZero(&entry);
 
-        entry.offset = frameOffset(fe, a) + (a->depth * sizeof(Value));
+        entry.offset = frameOffset(fe);
 
         if (fe->isCopy()) {
             FrameEntry *nfe = fe->copyOf();
             entry.copy = true;
-            entry.u.copiedOffset = frameOffset(nfe, a) + (a->depth * sizeof(Value));
+            entry.u.copiedOffset = frameOffset(nfe);
         } else if (fe->isConstant()) {
             entry.constant = true;
             entry.u.value = fe->getValue();
         } else if (fe->isTypeKnown() && !fe->isType(JSVAL_TYPE_DOUBLE) && !fe->type.synced()) {
             entry.knownType = true;
             entry.u.type = fe->getKnownType();
         } else {
             /*
@@ -2299,18 +2299,22 @@ FrameState::storeLocal(uint32 n, JSValue
     }
 
     storeTop(local, type, popGuaranteed);
 
     if (loop)
         local->lastLoop = loop->headOffset();
 
     if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE &&
-        fixedType && !a->parent && !local->type.synced()) {
-        /* Except when inlining, known types are always in sync for locals. */
+        fixedType && !local->type.synced()) {
+        /*
+         * Except when inlining, known types are always in sync for locals.
+         * If we are inlining, the known type is filled in when the frame is
+         * expanded (which happens upon any recompilation activity).
+         */
         local->type.sync();
     }
 
     if (inTryBlock)
         syncFe(local);
 }
 
 void
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -803,16 +803,19 @@ class FrameState
 #endif
 
     // Return an address, relative to the JSStackFrame, that represents where
     // this FrameEntry is stored in memory. Note that this is its canonical
     // address, not its backing store. There is no guarantee that the memory
     // is coherent.
     Address addressOf(const FrameEntry *fe) const { return addressOf(fe, a); }
     Address addressOf(uint32 slot) const { return addressOf(entries + slot); }
+    int32 frameOffset(const FrameEntry *fe) const {
+        return frameOffset(fe, a) + (a->depth * sizeof(Value));
+    }
 
     // Returns an address, relative to the JSStackFrame, that represents where
     // this FrameEntry is backed in memory. This is not necessarily its
     // canonical address, but the address for which the payload has been synced
     // to memory. The caller guarantees that the payload has been synced.
     Address addressForDataRemat(const FrameEntry *fe) const;
 
     // Inside an inline frame, the address for the return value in the caller.