[INFER] Watch out for incorrect SSA use chains in scripts where LOCAL ops alias stack values, bug 677635.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 09 Aug 2011 19:02:24 -0700
changeset 76108 8a7510ed55aa4d4034204395dc96a04da027e949
parent 76107 0a6ba466113f09af2959d0daaf641257e0283c8e
child 76109 06f56dc1a119aa726a0e5530a219350495942a1c
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
bugs677635
milestone8.0a1
[INFER] Watch out for incorrect SSA use chains in scripts where LOCAL ops alias stack values, bug 677635.
js/src/jit-test/tests/basic/bug677635.js
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsinfer.cpp
js/src/methodjit/LoopState.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug677635.js
@@ -0,0 +1,7 @@
+
+index = 0;
+test();
+function test() {
+  var [message,prettyPrinting]=[arguments,__lookupGetter__];
+  message<index;
+}
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -637,48 +637,61 @@ ScriptAnalysis::analyzeBytecode(JSContex
             /*
              * Watch for uses of variables not known to be defined, and mark
              * them as having possible uses before definitions.  Ignore GETLOCAL
              * followed by a POP, these are generated for, e.g. 'var x;'
              */
             jsbytecode *next = pc + JSOP_GETLOCAL_LENGTH;
             if (JSOp(*next) != JSOP_POP || jumpTarget(next)) {
                 uint32 local = GET_SLOTNO(pc);
-                if (local < script->nfixed && !localDefined(local, offset)) {
+                if (local >= script->nfixed) {
+                    localsAliasStack_ = true;
+                    break;
+                }
+                if (!localDefined(local, offset)) {
                     setLocal(local, LOCAL_USE_BEFORE_DEF);
                     isInlineable = false;
                 }
             }
             break;
           }
 
           case JSOP_CALLLOCAL:
           case JSOP_INCLOCAL:
           case JSOP_DECLOCAL:
           case JSOP_LOCALINC:
           case JSOP_LOCALDEC: {
             uint32 local = GET_SLOTNO(pc);
-            if (local < script->nfixed && !localDefined(local, offset)) {
+            if (local >= script->nfixed) {
+                localsAliasStack_ = true;
+                break;
+            }
+
+            if (!localDefined(local, offset)) {
                 setLocal(local, LOCAL_USE_BEFORE_DEF);
                 isInlineable = false;
             }
             break;
           }
 
           case JSOP_SETLOCAL: {
             uint32 local = GET_SLOTNO(pc);
+            if (local >= script->nfixed) {
+                localsAliasStack_ = true;
+                break;
+            }
 
             /*
              * The local variable may already have been marked as unconditionally
              * defined at a later point in the script, if that definition was in the
              * condition for a loop which then jumped back here.  In such cases we
              * will not treat the variable as ever being defined in the loop body
              * (see setLocal).
              */
-            if (local < script->nfixed && definedLocals[local] == LOCAL_CONDITIONALLY_DEFINED) {
+            if (definedLocals[local] == LOCAL_CONDITIONALLY_DEFINED) {
                 if (forwardJump) {
                     /* Add this local to the variables defined after this bytecode. */
                     uint32 *newArray = ArenaArray<uint32>(pool, defineCount + 1);
                     if (!newArray) {
                         setOOM(cx);
                         return;
                     }
                     if (defineCount)
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -889,16 +889,17 @@ class ScriptAnalysis
     bool usesRval;
     bool usesScope;
     bool usesThis;
     bool hasCalls;
     bool canTrackVars;
     bool isInlineable;
     uint32 numReturnSites_;
     bool modifiesArguments_;
+    bool localsAliasStack_;
 
     /* Offsets at which each local becomes unconditionally defined, or a value below. */
     uint32 *definedLocals;
 
     static const uint32 LOCAL_USE_BEFORE_DEF = uint32(-1);
     static const uint32 LOCAL_CONDITIONALLY_DEFINED = uint32(-2);
 
     /* --------- Lifetime analysis --------- */
@@ -937,16 +938,22 @@ class ScriptAnalysis
     uint32 numReturnSites() const { return numReturnSites_; }
 
     /*
      * True if all named formal arguments are not modified. If the arguments
      * object cannot escape, the arguments are never modified within the script.
      */
     bool modifiesArguments() { return modifiesArguments_; }
 
+    /*
+     * True if there are any LOCAL opcodes aliasing values on the stack (above
+     * script->nfixed).
+     */
+    bool localsAliasStack() { return localsAliasStack_; }
+
     /* Accessors for bytecode information. */
 
     Bytecode& getCode(uint32 offset) {
         JS_ASSERT(script->compartment->activeAnalysis);
         JS_ASSERT(offset < script->length);
         JS_ASSERT(codeArray[offset]);
         return *codeArray[offset];
     }
@@ -1072,16 +1079,20 @@ class ScriptAnalysis
     }
 
     bool trackUseChain(const SSAValue &v) {
         JS_ASSERT_IF(v.kind() == SSAValue::VAR, trackSlot(v.varSlot()));
         return v.kind() != SSAValue::EMPTY &&
             (v.kind() != SSAValue::VAR || !v.varInitial());
     }
 
+    /*
+     * Get the use chain for an SSA value. May be invalid for some opcodes in
+     * scripts where localsAliasStack(). You have been warned!
+     */
     SSAUseChain *& useChain(const SSAValue &v) {
         JS_ASSERT(trackUseChain(v));
         if (v.kind() == SSAValue::PUSHED)
             return getCode(v.pushedOffset()).pushedUses[v.pushedIndex()];
         if (v.kind() == SSAValue::VAR)
             return getCode(v.varOffset()).pushedUses[GetDefCount(script, v.varOffset())];
         return v.phiNode()->uses;
     }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3945,17 +3945,17 @@ ScriptAnalysis::analyzeTypes(JSContext *
 
     /*
      * Note: don't check for strict mode code here, even though arguments
      * accesses in such scripts will always be deoptimized. These scripts can
      * have a JSOP_ARGUMENTS in their prologue which the usesArguments check
      * above does not account for. We filter in the interpreter and JITs
      * themselves.
      */
-    if (script->function()->isHeavyweight() || cx->compartment->debugMode) {
+    if (script->function()->isHeavyweight() || cx->compartment->debugMode || localsAliasStack()) {
         MarkArgumentsCreated(cx, script);
         return;
     }
 
     offset = 0;
     while (offset < script->length) {
         Bytecode *code = maybeCode(offset);
         jsbytecode *pc = script->code + offset;
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -1763,17 +1763,17 @@ LoopState::analyzeLoopBody(unsigned fram
      * inlined frame plus another stack frame pushed by, e.g. ic::Call.
      * This new frame will have been partially initialized by the call, and
      * we don't want to scribble on that frame when restoring invariants.
      */
     temporariesStart =
         Max<uint32>(temporariesStart,
                     ssa->getFrame(frame).depth + VALUES_PER_STACK_FRAME * 2 + script->nslots);
 
-    if (script->failedBoundsCheck)
+    if (script->failedBoundsCheck || analysis->localsAliasStack())
         skipAnalysis = true;
 
     /* Analyze the entire script for frames inlined in the loop body. */
     unsigned start = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->head + JSOP_TRACE_LENGTH : 0;
     unsigned end = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->backedge : script->length;
 
     unsigned offset = start;
     while (offset < end) {