Bug 495773: include trace entry JSStackFrame in upvar search, also remove obsolete union from FrameInfo, r=gal
authorDavid Mandelin <dmandelin@mozilla.com>
Mon, 01 Jun 2009 14:50:02 -0700
changeset 25863 71c7ae28fa74623de4bc6607b701674e289793e3
parent 25862 cd581b2585bc07826acb5193bd2e16d4dcd9ceed
child 25864 e292a34f8844dbd0f01efe63adc755e9d2e462fb
push id1645
push userrsayre@mozilla.com
push dateThu, 04 Jun 2009 16:54:53 +0000
reviewersgal
bugs495773
milestone1.9.1pre
Bug 495773: include trace entry JSStackFrame in upvar search, also remove obsolete union from FrameInfo, r=gal
js/src/jstracer.cpp
js/src/jstracer.h
js/tests/js1_8_1/regress/regress-495773.js
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -1841,16 +1841,29 @@ FlushNativeGlobalFrame(JSContext* cx, un
         NativeToValue(cx, *vp, *mp, np + gslots[n]);
         ++mp;
     );
     debug_only_v(printf("\n");)
     return mp - mp_base;
 }
 
 /*
+ * Helper for js_GetUpvarOnTrace.
+ */
+static uint32 
+GetUpvarOnTraceTail(InterpState* state, uint32 cookie,
+                    uint32 nativeStackFramePos, uint8* typemap, double* result)
+{
+    uintN slot = UPVAR_FRAME_SLOT(cookie);
+    slot = (slot == CALLEE_UPVAR_SLOT) ? 0 : 2/*callee,this*/ + slot;
+    *result = state->stackBase[nativeStackFramePos + slot];
+    return typemap[slot];
+}
+
+/*
  * Builtin to get an upvar on trace. See js_GetUpvar for the meaning
  * of the first three arguments. The value of the upvar is stored in
  * *result as an unboxed native. The return value is the typemap type.
  */
 uint32 JS_FASTCALL
 js_GetUpvarOnTrace(JSContext* cx, uint32 level, uint32 cookie, uint32 callDepth, double* result)
 {
     uintN skip = UPVAR_FRAME_SKIP(cookie);
@@ -1870,25 +1883,27 @@ js_GetUpvarOnTrace(JSContext* cx, uint32
             /*
              * Now find the upvar's value in the native stack.
              * nativeStackFramePos is the offset of the start of the 
              * activation record corresponding to *fip in the native
              * stack.
              */
             uintN nativeStackFramePos = state->callstackBase[0]->spoffset;
             for (FrameInfo** fip2 = state->callstackBase; fip2 <= fip; fip2++)
-                nativeStackFramePos += (*fip2)->s.spdist;
-            nativeStackFramePos -= (2 + (*fip)->s.argc);
+                nativeStackFramePos += (*fip2)->spdist;
+            nativeStackFramePos -= (2 + (*fip)->argc);
             uint8* typemap = (uint8*) (fi+1);
-
-            uintN slot = UPVAR_FRAME_SLOT(cookie);
-            slot = (slot == CALLEE_UPVAR_SLOT) ? 0 : 2/*callee,this*/ + slot;
-            *result = state->stackBase[nativeStackFramePos + slot];
-            return typemap[slot];
-        }
+            return GetUpvarOnTraceTail(state, cookie, nativeStackFramePos,
+                                       typemap, result);
+        }
+    }
+
+    if (cx->fp->fun && cx->fp->fun->u.i.script->staticLevel == upvarLevel) {
+        return GetUpvarOnTraceTail(state, cookie, 0, 
+                                   state->outermostTree->stackTypeMap(), result);
     }
 
     /*
      * If we did not find the upvar in the frames for the active traces,
      * then we simply get the value from the interpreter state.
      */
     jsval v = js_GetUpvar(cx, level, cookie);
     uint8 type = getCoercedType(v);
@@ -3525,27 +3540,27 @@ js_SynthesizeFrame(JSContext* cx, const 
 
     JSFunction* fun = GET_FUNCTION_PRIVATE(cx, fi.callee);
     JS_ASSERT(FUN_INTERPRETED(fun));
 
     /* Assert that we have a correct sp distance from cx->fp->slots in fi. */
     JSStackFrame* fp = cx->fp;
     JS_ASSERT_IF(!fi.imacpc,
                  js_ReconstructStackDepth(cx, fp->script, fi.pc)
-                 == uintN(fi.s.spdist - fp->script->nfixed));
+                 == uintN(fi.spdist - fp->script->nfixed));
 
     uintN nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval));
     JSScript* script = fun->u.i.script;
     size_t nbytes = (nframeslots + script->nslots) * sizeof(jsval);
 
     /* Code duplicated from inline_call: case in js_Interpret (FIXME). */
     JSArena* a = cx->stackPool.current;
     void* newmark = (void*) a->avail;
-    uintN argc = fi.s.argc & 0x7fff;
-    jsval* vp = fp->slots + fi.s.spdist - (2 + argc);
+    uintN argc = fi.argc & 0x7fff;
+    jsval* vp = fp->slots + fi.spdist - (2 + argc);
     uintN missing = 0;
     jsval* newsp;
 
     if (fun->nargs > argc) {
         const JSFrameRegs& regs = *fp->regs;
 
         newsp = vp + 2 + fun->nargs;
         JS_ASSERT(newsp > regs.sp);
@@ -3593,20 +3608,20 @@ js_SynthesizeFrame(JSContext* cx, const 
 
     newifp->frame.callobj = NULL;
     newifp->frame.argsobj = NULL;
     newifp->frame.varobj = NULL;
     newifp->frame.script = script;
     newifp->frame.callee = fi.callee; // Roll with a potentially stale callee for now.
     newifp->frame.fun = fun;
 
-    bool constructing = (fi.s.argc & 0x8000) != 0;
+    bool constructing = (fi.argc & 0x8000) != 0;
     newifp->frame.argc = argc;
     newifp->callerRegs.pc = fi.pc;
-    newifp->callerRegs.sp = fp->slots + fi.s.spdist;
+    newifp->callerRegs.sp = fp->slots + fi.spdist;
     fp->imacpc = fi.imacpc;
 
 #ifdef DEBUG
     if (fi.block != fp->blockChain) {
         for (JSObject* obj = fi.block; obj != fp->blockChain; obj = STOBJ_GET_PARENT(obj))
             JS_ASSERT(obj);
     }
 #endif
@@ -3664,17 +3679,17 @@ js_SynthesizeFrame(JSContext* cx, const 
         newifp->hookData = hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData);
     } else {
         newifp->hookData = NULL;
     }
 
     // FIXME? we must count stack slots from caller's operand stack up to (but not including)
     // callee's, including missing arguments. Could we shift everything down to the caller's
     // fp->slots (where vars start) and avoid some of the complexity?
-    return (fi.s.spdist - fp->down->script->nfixed) +
+    return (fi.spdist - fp->down->script->nfixed) +
            ((fun->nargs > fp->argc) ? fun->nargs - fp->argc : 0) +
            script->nfixed;
 }
 
 static void
 SynthesizeSlowNativeFrame(JSContext *cx, VMSideExit *exit)
 {
     void *mark;
@@ -8475,25 +8490,25 @@ TraceRecorder::interpretedFunctionCall(j
 
     if (argc >= 0x8000)
         ABORT_TRACE("too many arguments");
 
     fi->callee = JSVAL_TO_OBJECT(fval);
     fi->block = fp->blockChain;
     fi->pc = fp->regs->pc;
     fi->imacpc = fp->imacpc;
-    fi->s.spdist = fp->regs->sp - fp->slots;
-    fi->s.argc = argc | (constructing ? 0x8000 : 0);
+    fi->spdist = fp->regs->sp - fp->slots;
+    fi->argc = argc | (constructing ? 0x8000 : 0);
     fi->spoffset = 2 /*callee,this*/ + fp->argc;
 
     unsigned callDepth = getCallDepth();
     if (callDepth >= treeInfo->maxCallDepth)
         treeInfo->maxCallDepth = callDepth + 1;
     if (callDepth == 0)
-        fi->spoffset = 2 /*callee,this*/ + argc - fi->s.spdist;
+        fi->spoffset = 2 /*callee,this*/ + argc - fi->spdist;
 
     lir->insStorei(INS_CONSTPTR(fi), lirbuf->rp, callDepth * sizeof(FrameInfo*));
 
     atoms = fun->u.i.script->atomMap.vector;
     return JSRS_CONTINUE;
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -287,23 +287,19 @@ static inline uint8* getFullTypeMap(nano
     return getStackTypeMap(exit);
 }
 
 struct FrameInfo {
     JSObject*       callee;     // callee function object
     JSObject*       block;      // caller block chain head
     jsbytecode*     pc;         // caller fp->regs->pc
     jsbytecode*     imacpc;     // caller fp->imacpc
-    union {
-        struct {
-            uint16  spdist;     // distance from fp->slots to fp->regs->sp at JSOP_CALL
-            uint16  argc;       // actual argument count, may be < fun->nargs
-        } s;
-        uint32      word;       // for spdist/argc LIR store in record_JSOP_CALL
-    };
+    uint16          spdist;     // distance from fp->slots to fp->regs->sp at JSOP_CALL
+    uint16          argc;       // actual argument count, may be < fun->nargs
+
     /*
      * Stack pointer adjustment needed for navigation of native stack in
      * js_GetUpvarOnTrace. spoffset is the number of slots in the native
      * stack frame for the caller *before* the slots covered by spdist.
      * This may be negative if the caller is the top level script.
      * The key fact is that if we let 'cpos' be the start of the caller's
      * native stack frame, then (cpos + spoffset) points to the first 
      * non-argument slot in the callee's native stack frame.
new file mode 100644
--- /dev/null
+++ b/js/tests/js1_8_1/regress/regress-495773.js
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is JavaScript Engine testing utilities.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Dave Mandelin
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var gTestfile = 'regress-495773.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 495773;
+var summary = 'Read upvar from trace-entry frame from JSStackFrame instead of tracing native stack';
+var actual = ''
+var expect = '010101';
+//-----------------------------------------------------------------------------
+function f() {
+    var q = [];
+    for (var a = 0; a < 3; ++a) {
+        (function () {
+            for (var b = 0; b < 2; ++b) {
+                (function () {
+                    for (var c = 0; c < 1; ++c) {
+                        q.push(b);
+                    }
+                })();
+            }
+        })();
+    }
+    return q.join("");
+}
+
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  enterFunc ('test');
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+
+  jit(true);
+  actual = f();
+  jit(false);
+
+  reportCompare(expect, actual, summary);
+
+  exitFunc ('test');
+}