Bug 577708: Remove Algol-like display optimization. (r=dvander)
authorChris Leary <cdleary@mozilla.com>
Sun, 11 Jul 2010 00:57:13 -0700
changeset 53091 90da6c5c9cbbfc735159314a7033723b46b825a2
parent 53090 ed16446defd4711d1225b7107a71fb28664dbe37
child 53092 c608b7bf243fcdd35f5bf219859d5822f4812360
child 53101 568850e1f3a1524ad50d6b6ba3355619d75bb252
push id15660
push userrsayre@mozilla.com
push dateSat, 11 Sep 2010 19:16:24 +0000
treeherdermozilla-central@f1bd314e64ac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs577708
milestone2.0b2pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 577708: Remove Algol-like display optimization. (r=dvander)
js/src/jsbuiltins.cpp
js/src/jscntxt.h
js/src/jsdbgapi.cpp
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsfun.cpp
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsobj.cpp
js/src/jsops.cpp
js/src/jsparse.cpp
js/src/jsparse.h
js/src/jsscript.h
js/src/jstracer.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/StubCalls.cpp
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -349,20 +349,16 @@ js_PopInterpFrame(JSContext* cx, TracerS
         return JS_FALSE;
     if (fp->imacpc)
         return JS_FALSE;
     if (fp->blockChain)
         return JS_FALSE;
 
     fp->putActivationObjects(cx);
     
-    /* Update display table. */
-    if (fp->script->staticLevel < JS_DISPLAY_SIZE)
-        cx->display[fp->script->staticLevel] = fp->displaySave;
-
     /* Pop the frame and its memory. */
     cx->stack().popInlineFrame(cx, fp, fp->down);
 
     /* Update the inline call count. */
     *state->inlineCallCountp = *state->inlineCallCountp - 1;
     return JS_TRUE;
 }
 JS_DEFINE_CALLINFO_2(extern, BOOL, js_PopInterpFrame, CONTEXT, TRACERSTATE, 0, ACC_STORE_ANY)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1719,23 +1719,16 @@ struct JSContext
      */
     volatile jsword     interruptFlags;
 
     static const jsword INTERRUPT_OPERATION_CALLBACK = 0x1;
 
     /* JSRuntime contextList linkage. */
     JSCList             link;
 
-    /*
-     * Classic Algol "display" static link optimization.
-     */
-#define JS_DISPLAY_SIZE 16U
-
-    JSStackFrame        *display[JS_DISPLAY_SIZE];
-
     /* Runtime version control identifier. */
     uint16              version;
 
     /* Per-context options. */
     uint32              options;            /* see jsapi.h for JSOPTION_* */
 
     /* Locale specific callbacks for string conversion. */
     JSLocaleCallbacks   *localeCallbacks;
@@ -3046,16 +3039,32 @@ CanLeaveTrace(JSContext *cx)
     JS_ASSERT(JS_ON_TRACE(cx));
 #ifdef JS_TRACER
     return cx->bailExit != NULL;
 #else
     return JS_FALSE;
 #endif
 }
 
+/*
+ * Search the call stack for the nearest frame with static level targetLevel.
+ * @param baseFrame If not provided, the context's current frame is used.
+ */
+static JS_INLINE JSStackFrame *
+FindFrameAtLevel(JSContext *cx, uint16 targetLevel, JSStackFrame * const baseFrame = NULL)
+{
+    JSStackFrame *it = baseFrame ? baseFrame : cx->fp;
+    JS_ASSERT(it && it->script);
+    while (it->script->staticLevel != targetLevel) {
+        it = it->down;
+        JS_ASSERT(it && it->script);
+    }
+    return it;
+}
+
 extern void
 SetPendingException(JSContext *cx, const Value &v);
 
 } /* namespace js */
 
 /*
  * Get the current cx->fp, first lazily instantiating stack frames if needed.
  * (Do not access cx->fp directly except in JS_REQUIRES_STACK code.)
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1338,79 +1338,36 @@ JS_SetDestroyScriptHook(JSRuntime *rt, J
 JS_PUBLIC_API(JSBool)
 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
                           const jschar *chars, uintN length,
                           const char *filename, uintN lineno,
                           jsval *rval)
 {
     JS_ASSERT_NOT_ON_TRACE(cx);
 
-    JSObject *scobj;
-    JSScript *script;
-    JSBool ok;
-
-    scobj = JS_GetFrameScopeChain(cx, fp);
+    JSObject *scobj = JS_GetFrameScopeChain(cx, fp);
     if (!scobj)
         return JS_FALSE;
 
     /*
      * NB: This function breaks the assumption that the compiler can see all
      * calls and properly compute a static level. In order to get around this,
      * we use a static level that will cause us not to attempt to optimize
      * variable references made by this frame.
      */
-    script = Compiler::compileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
-                                     TCF_COMPILE_N_GO, chars, length, NULL,
-                                     filename, lineno, NULL, JS_DISPLAY_SIZE);
+    JSScript *script = Compiler::compileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
+                                               TCF_COMPILE_N_GO, chars, length, NULL, filename,
+                                               lineno, NULL, UpvarCookie::MAX_LEVEL);
 
     if (!script)
         return JS_FALSE;
 
-    JSStackFrame *displayCopy[JS_DISPLAY_SIZE];
-    if (cx->fp != fp) {
-        memcpy(displayCopy, cx->display, sizeof displayCopy);
+    bool ok = !!Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL,
+                        Valueify(rval));
 
-        /*
-         * Set up cx->display as it would have been when fp was active.
-         *
-         * NB: To reconstruct cx->display for fp, we have to follow the frame
-         * chain from oldest to youngest, in the opposite direction to its
-         * single linkge. To avoid the obvious recursive reversal algorithm,
-         * which might use too much stack, we reverse in place and reverse
-         * again as we reconstruct the display. This is safe because cx is
-         * thread-local and we can't cause GC until the call to js_Execute
-         * below.
-         */
-        JSStackFrame *fp2 = fp, *last = NULL;
-        while (fp2) {
-            JSStackFrame *next = fp2->down;
-            fp2->down = last;
-            last = fp2;
-            fp2 = next;
-        }
-
-        fp2 = last;
-        last = NULL;
-        while (fp2) {
-            JSStackFrame *next = fp2->down;
-            fp2->down = last;
-            last = fp2;
-
-            JSScript *script = fp2->script;
-            if (script && script->staticLevel < JS_DISPLAY_SIZE)
-                cx->display[script->staticLevel] = fp2;
-            fp2 = next;
-        }
-    }
-
-    ok = Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL,
-                 Valueify(rval));
-
-    if (cx->fp != fp)
-        memcpy(cx->display, displayCopy, sizeof cx->display);
     js_DestroyScript(cx, script);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
                         const char *bytes, uintN length,
                         const char *filename, uintN lineno,
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -1910,17 +1910,17 @@ MakeUpvarForEval(JSParseNode *pn, JSCode
     JSAtom *atom = pn->pn_atom;
 
     uintN index;
     JSLocalKind localKind = js_LookupLocal(cx, fun, atom, &index);
     if (localKind == JSLOCAL_NONE)
         return true;
 
     JS_ASSERT(cg->staticLevel > upvarLevel);
-    if (cg->staticLevel >= JS_DISPLAY_SIZE || upvarLevel >= JS_DISPLAY_SIZE)
+    if (cg->staticLevel >= UpvarCookie::MAX_LEVEL || upvarLevel >= UpvarCookie::MAX_LEVEL)
         return true;
 
     JSAtomListElement *ale = cg->upvarList.lookup(atom);
     if (!ale) {
         if (cg->inFunction() &&
             !js_AddLocal(cx, cg->fun, atom, JSLOCAL_UPVAR)) {
             return false;
         }
@@ -2159,17 +2159,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
                 return JS_TRUE;
         }
         pn->pn_op = op;
         pn->pn_cookie.set(cookie);
         pn->pn_dflags |= PND_BOUND;
         return JS_TRUE;
     }
 
-    uintN level = cookie.level();
+    uint16 level = cookie.level();
     JS_ASSERT(cg->staticLevel >= level);
 
     /*
      * A JSDefinition witnessed as a declaration by the parser cannot be an
      * upvar, unless it is the degenerate kind of upvar selected above (in the
      * code before the PND_GVAR test) for the special case of compile-and-go
      * code generated from eval called from a function, where the eval code
      * uses local vars defined in the function. We detect this upvar-for-eval
@@ -2207,31 +2207,31 @@ BindNameToSlot(JSContext *cx, JSCodeGene
             pn->pn_cookie = cookie;
             pn->pn_dflags |= PND_BOUND;
             return JS_TRUE;
         }
 
         return MakeUpvarForEval(pn, cg);
     }
 
-    uintN skip = cg->staticLevel - level;
+    uint16 skip = cg->staticLevel - level;
     if (skip != 0) {
         JS_ASSERT(cg->inFunction());
         JS_ASSERT_IF(cookie.slot() != UpvarCookie::CALLEE_SLOT, cg->lexdeps.lookup(atom));
         JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
         JS_ASSERT(cg->fun->u.i.skipmin <= skip);
 
         /*
          * If op is a mutating opcode, this upvar's static level is too big to
          * index into the display, or the function is heavyweight, we fall back
          * on JSOP_*NAME*.
          */
         if (op != JSOP_NAME)
             return JS_TRUE;
-        if (level >= JS_DISPLAY_SIZE)
+        if (level >= UpvarCookie::MAX_LEVEL)
             return JS_TRUE;
         if (cg->flags & TCF_FUN_HEAVYWEIGHT)
             return JS_TRUE;
 
         if (FUN_FLAT_CLOSURE(cg->fun)) {
             op = JSOP_GETDSLOT;
         } else {
             /*
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -286,17 +286,17 @@ struct JSTreeContext {              /* t
     union {
         JSFunction  *fun;           /* function to store argument and variable
                                        names when flags & TCF_IN_FUNCTION */
         JSObject    *scopeChain;    /* scope chain object for the script */
     };
 
     JSAtomList      lexdeps;        /* unresolved lexical name dependencies */
     JSTreeContext   *parent;        /* enclosing function or global context */
-    uintN           staticLevel;    /* static compilation unit nesting level */
+    uint16          staticLevel;    /* static compilation unit nesting level */
 
     JSFunctionBox   *funbox;        /* null or box for function we're compiling
                                        if (flags & TCF_IN_FUNCTION) and not in
                                        Compiler::compileFunctionBody */
     JSFunctionBox   *functionList;
 
     JSParseNode     *innermostWith; /* innermost WITH parse node */
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2497,19 +2497,19 @@ js_NewFlatClosure(JSContext *cx, JSFunct
 
     JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
     if (!closure || fun->u.i.nupvars == 0)
         return closure;
 
     JSUpvarArray *uva = fun->u.i.script->upvars();
     JS_ASSERT(uva->length <= closure->dslots[-1].toPrivateUint32());
 
-    uintN level = fun->u.i.script->staticLevel;
+    uint16 level = fun->u.i.script->staticLevel;
     for (uint32 i = 0, n = uva->length; i < n; i++)
-        closure->dslots[i] = js_GetUpvar(cx, level, uva->vector[i]);
+        closure->dslots[i] = GetUpvar(cx, level, uva->vector[i]);
 
     return closure;
 }
 
 JSObject *
 js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun)
 {
     JS_ASSERT(cx->fp->fun->flags & JSFUN_HEAVYWEIGHT);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -425,28 +425,21 @@ class AutoPreserveEnumerators {
 
 struct AutoInterpPreparer  {
     JSContext *cx;
     JSScript *script;
 
     AutoInterpPreparer(JSContext *cx, JSScript *script)
       : cx(cx), script(script)
     {
-        if (script->staticLevel < JS_DISPLAY_SIZE) {
-            JSStackFrame **disp = &cx->display[script->staticLevel];
-            cx->fp->displaySave = *disp;
-            *disp = cx->fp;
-        }
         cx->interpLevel++;
     }
 
     ~AutoInterpPreparer()
     {
-        if (script->staticLevel < JS_DISPLAY_SIZE)
-            cx->display[script->staticLevel] = cx->fp->displaySave;
         --cx->interpLevel;
     }
 };
 
 JS_REQUIRES_STACK bool
 RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain)
 {
     JS_ASSERT(script);
@@ -546,17 +539,16 @@ InvokeCommon(JSContext *cx, JSFunction *
     fp->argc = argc;
     fp->argv = vp + 2;
     fp->rval = (flags & JSINVOKE_CONSTRUCT) ? fp->thisv : UndefinedValue();
     fp->annotation = NULL;
     fp->scopeChain = NULL;
     fp->blockChain = NULL;
     fp->imacpc = NULL;
     fp->flags = flags;
-    fp->displaySave = NULL;
 
     /* Initialize regs. */
     if (script) {
         regs.pc = script->code;
         regs.sp = fp->slots() + script->nfixed;
     } else {
         regs.pc = NULL;
         regs.sp = fp->slots();
@@ -1378,27 +1370,24 @@ js_DoIncDec(JSContext *cx, const JSCodeS
         return JS_FALSE;
     (cs->format & JOF_INC) ? ++d : --d;
     vp->setNumber(d);
     *vp2 = *vp;
     return JS_TRUE;
 }
 
 const Value &
-js_GetUpvar(JSContext *cx, uintN level, UpvarCookie cookie)
+js::GetUpvar(JSContext *cx, uint16 closureLevel, UpvarCookie cookie)
 {
-    level -= cookie.level();
-    JS_ASSERT(level < JS_DISPLAY_SIZE);
+    JS_ASSERT(closureLevel >= cookie.level() && cookie.level() > 0);
+    const uint16 targetLevel = closureLevel - cookie.level();
+    JSStackFrame *fp = FindFrameAtLevel(cx, targetLevel);
 
-    JSStackFrame *fp = cx->display[level];
-    JS_ASSERT(fp->script);
-
-    uintN slot = cookie.slot();
+    uint16 slot = cookie.slot();
     Value *vp;
-
     if (!fp->fun || (fp->flags & JSFRAME_EVAL)) {
         vp = fp->slots() + fp->script->nfixed;
     } else if (slot < fp->fun->nargs) {
         vp = fp->argv;
     } else if (slot == UpvarCookie::CALLEE_SLOT) {
         vp = &fp->argv[-2];
         slot = 0;
     } else {
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -155,18 +155,16 @@ struct JSStackFrame
      * This lazy cloning is implemented in js_GetScopeChain, which is
      * also used in some other cases --- entering 'with' blocks, for
      * example.
      */
     JSObject        *scopeChain;
     JSObject        *blockChain;
 
     uint32          flags;          /* frame flags -- see below */
-    JSStackFrame    *displaySave;   /* previous value of display entry for
-                                       script->staticLevel */
 
     /* Members only needed for inline calls. */
     void            *hookData;      /* debugger call hook data */
     JSVersion       callerVersion;  /* dynamic version of calling script */
 
     void putActivationObjects(JSContext *cx) {
         /*
          * The order of calls here is important as js_PutCallObject needs to
@@ -406,24 +404,27 @@ GetInstancePrivate(JSContext *cx, JSObje
     if (!InstanceOf(cx, obj, clasp, argv))
         return NULL;
     return obj->getPrivate();
 }
 
 extern bool
 ValueToId(JSContext *cx, const Value &v, jsid *idp);
 
-} /* namespace js */
-
 /*
- * Given an active context, a static scope level, and an upvar cookie, return
- * the value of the upvar.
+ * @param closureLevel      The static level of the closure that the cookie
+ *                          pertains to.
+ * @param cookie            Level amount is a "skip" (delta) value from the
+ *                          closure level.
+ * @return  The value of the upvar.
  */
 extern const js::Value &
-js_GetUpvar(JSContext *cx, uintN level, js::UpvarCookie cookie);
+GetUpvar(JSContext *cx, uint16 closureLevel, js::UpvarCookie cookie);
+
+} /* namespace js */
 
 /*
  * JS_LONE_INTERPRET indicates that the compiler should see just the code for
  * the js_Interpret function when compiling jsinterp.cpp. The rest of the code
  * from the file should be visible only when compiling jsinvoke.cpp. It allows
  * platform builds to optimize selectively js_Interpret when the granularity
  * of the optimizations with the given compiler is a compilation unit.
  *
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -6625,19 +6625,16 @@ js_DumpStackFrame(JSContext *cx, JSStack
             fprintf(stderr, " overridden_args");
         fputc('\n', stderr);
 
         if (fp->scopeChain)
             fprintf(stderr, "  scopeChain: (JSObject *) %p\n", (void *) fp->scopeChain);
         if (fp->blockChain)
             fprintf(stderr, "  blockChain: (JSObject *) %p\n", (void *) fp->blockChain);
 
-        if (fp->displaySave)
-            fprintf(stderr, "  displaySave: (JSStackFrame *) %p\n", (void *) fp->displaySave);
-
         fputc('\n', stderr);
     }
 }
 
 #ifdef DEBUG
 bool
 IsSaneThisObject(JSObject &obj)
 {
--- a/js/src/jsops.cpp
+++ b/js/src/jsops.cpp
@@ -230,19 +230,16 @@ BEGIN_CASE(JSOP_STOP)
 
     interpReturnOK = true;
     if (inlineCallCount)
   inline_return:
     {
         JS_ASSERT(!fp->blockChain);
         JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
 
-        if (JS_LIKELY(script->staticLevel < JS_DISPLAY_SIZE))
-            cx->display[script->staticLevel] = fp->displaySave;
-
         void *hookData = fp->hookData;
         if (JS_UNLIKELY(hookData != NULL)) {
             if (JSInterpreterHook hook = cx->debugHooks->callHook) {
                 hook(cx, fp, JS_FALSE, &interpReturnOK, hookData);
                 CHECK_INTERRUPT_HANDLER();
             }
         }
 
@@ -2248,21 +2245,16 @@ BEGIN_CASE(JSOP_APPLY)
             newfp->fun = fun;
             newfp->argc = argc;
             newfp->argv = vp + 2;
             newfp->rval = UndefinedValue();
             newfp->annotation = NULL;
             newfp->scopeChain = obj->getParent();
             newfp->flags = flags;
             newfp->blockChain = NULL;
-            if (JS_LIKELY(newscript->staticLevel < JS_DISPLAY_SIZE)) {
-                JSStackFrame **disp = &cx->display[newscript->staticLevel];
-                newfp->displaySave = *disp;
-                *disp = newfp;
-            }
             JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
             JS_ASSERT_IF(!vp[1].isPrimitive(), IsSaneThisObject(vp[1].toObject()));
             newfp->thisv = vp[1];
             newfp->imacpc = NULL;
 
             /* Push void to initialize local variables. */
             Value *newsp = newfp->base();
             SetValueRangeToUndefined(newfp->slots(), newsp);
@@ -2850,17 +2842,17 @@ END_SET_CASE(JSOP_SETLOCAL)
 BEGIN_CASE(JSOP_GETUPVAR)
 BEGIN_CASE(JSOP_CALLUPVAR)
 {
     JSUpvarArray *uva = script->upvars();
 
     uintN index = GET_UINT16(regs.pc);
     JS_ASSERT(index < uva->length);
 
-    const Value &rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]);
+    const Value &rval = GetUpvar(cx, script->staticLevel, uva->vector[index]);
     PUSH_COPY(rval);
 
     if (op == JSOP_CALLUPVAR)
         PUSH_NULL();
 }
 END_CASE(JSOP_GETUPVAR)
 
 BEGIN_CASE(JSOP_GETUPVAR_DBG)
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -690,17 +690,17 @@ Parser::parse(JSObject *chain)
         }
     }
     return pn;
 }
 
 JS_STATIC_ASSERT(UpvarCookie::FREE_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
 
 static inline bool
-SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
+SetStaticLevel(JSTreeContext *tc, uint16 staticLevel)
 {
     /*
      * This is a lot simpler than error-checking every UpvarCookie::set, and
      * practically speaking it leaves more than enough room for upvars.
      */
     if (UpvarCookie::isLevelReserved(staticLevel)) {
         JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
                              JSMSG_TOO_DEEP, js_function_str);
@@ -714,17 +714,17 @@ SetStaticLevel(JSTreeContext *tc, uintN 
  * Compile a top-level script.
  */
 JSScript *
 Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
                         JSPrincipals *principals, uint32 tcflags,
                         const jschar *chars, size_t length,
                         FILE *file, const char *filename, uintN lineno,
                         JSString *source /* = NULL */,
-                        unsigned staticLevel /* = 0 */)
+                        uint16 staticLevel /* = 0 */)
 {
     JSArenaPool codePool, notePool;
     TokenKind tt;
     JSParseNode *pn;
     uint32 scriptGlobals;
     JSScript *script;
     bool inDirectivePrologue;
 #ifdef METER_PARSENODES
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -1126,17 +1126,17 @@ struct Compiler
                         const char *filename, uintN lineno);
 
     static JSScript *
     compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
                   JSPrincipals *principals, uint32 tcflags,
                   const jschar *chars, size_t length,
                   FILE *file, const char *filename, uintN lineno,
                   JSString *source = NULL,
-                  unsigned staticLevel = 0);
+                  uint16 staticLevel = 0);
 };
 
 } /* namespace js */
 
 /*
  * Convenience macro to access Parser.tokenStream as a pointer.
  */
 #define TS(p) (&(p)->tokenStream)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -79,16 +79,17 @@ class UpvarCookie
     static const uint32 FREE_VALUE = 0xfffffffful;
 
   public:
     /*
      * All levels above-and-including FREE_LEVEL are reserved so that
      * FREE_VALUE can be used as a special value.
      */
     static const uint16 FREE_LEVEL = 0x3fff;
+    static const uint16 MAX_LEVEL = FREE_LEVEL - 1;
     static const uint16 CALLEE_SLOT = 0xffff;
     static bool isLevelReserved(uint16 level) { return level >= FREE_LEVEL; }
 
     bool isFree() const { return value == FREE_VALUE; }
     uint32 asInteger() const { return value; }
     /* isFree check should be performed before using these accessors. */
     uint16 level() const { JS_ASSERT(!isFree()); return value >> 16; }
     uint16 slot() const { JS_ASSERT(!isFree()); return value; }
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -3107,18 +3107,18 @@ GetUpvarOnTrace(JSContext* cx, uint32 up
         *result = state->stackBase[native_slot];
         return state->callstackBase[0]->get_typemap()[native_slot];
     }
 
     /*
      * If we did not find the upvar in the frames for the active traces,
      * then we simply get the value from the interpreter state.
      */
-    JS_ASSERT(upvarLevel < JS_DISPLAY_SIZE);
-    JSStackFrame* fp = cx->display[upvarLevel];
+    JS_ASSERT(upvarLevel < UpvarCookie::FREE_LEVEL);
+    JSStackFrame* fp = FindFrameAtLevel(cx, upvarLevel);
     Value v = T::interp_get(fp, slot);
     JSValueType type = getCoercedType(v);
     ValueToNative(v, type, result);
     return type;
 }
 
 // For this traits type, 'slot' is the argument index, which may be -2 for callee.
 struct UpvarArgTraits {
@@ -5632,21 +5632,16 @@ SynthesizeFrame(JSContext* cx, const Fra
 #endif
     newfp->rval = UndefinedValue();
     newfp->annotation = NULL;
     newfp->scopeChain = NULL; // will be updated in FlushNativeStackFrame
     newfp->flags = fi.is_constructing() ? JSFRAME_CONSTRUCTING : 0;
     newfp->blockChain = NULL;
     newfp->thisv.setNull(); // will be updated in FlushNativeStackFrame
     newfp->imacpc = NULL;
-    if (newscript->staticLevel < JS_DISPLAY_SIZE) {
-        JSStackFrame **disp = &cx->display[newscript->staticLevel];
-        newfp->displaySave = *disp;
-        *disp = newfp;
-    }
 
     /*
      * Note that fp->script is still the caller's script; set the callee
      * inline frame's idea of caller version from its version.
      */
     newfp->callerVersion = (JSVersion) fp->script->version;
 
     /* Push inline frame. (Copied from js_Interpret.) */
@@ -5708,17 +5703,16 @@ SynthesizeSlowNativeFrame(TracerState& s
     fp->argv = state.nativeVp + 2;
     fp->fun = GET_FUNCTION_PRIVATE(cx, fp->callee());
     fp->rval = UndefinedValue();
     fp->annotation = NULL;
     JS_ASSERT(cx->fp->scopeChain);
     fp->scopeChain = cx->fp->scopeChain;
     fp->blockChain = NULL;
     fp->flags = exit->constructing() ? JSFRAME_CONSTRUCTING : 0;
-    fp->displaySave = NULL;
 
     state.bailedSlowNativeRegs = *cx->regs;
 
     cx->stack().pushSynthesizedSlowNativeFrame(cx, cs, fp, state.bailedSlowNativeRegs);
 
     state.bailedSlowNativeRegs.pc = NULL;
     state.bailedSlowNativeRegs.sp = fp->slots();
 }
@@ -12920,35 +12914,35 @@ JS_DEFINE_CALLINFO_5(extern, UINT32, Get
  * value. NULL is returned only on a can't-happen condition with an invalid
  * typemap. The value of the upvar is returned as v.
  */
 JS_REQUIRES_STACK LIns*
 TraceRecorder::upvar(JSScript* script, JSUpvarArray* uva, uintN index, Value& v)
 {
     /*
      * Try to find the upvar in the current trace's tracker. For &vr to be
-     * the address of the jsval found in js_GetUpvar, we must initialize
+     * the address of the jsval found in js::GetUpvar, we must initialize
      * vr directly with the result, so it is a reference to the same location.
      * It does not work to assign the result to v, because v is an already
      * existing reference that points to something else.
      */
     UpvarCookie cookie = uva->vector[index];
-    const Value& vr = js_GetUpvar(cx, script->staticLevel, cookie);
+    const Value& vr = GetUpvar(cx, script->staticLevel, cookie);
     v = vr;
 
     if (LIns* ins = attemptImport(&vr))
         return ins;
 
     /*
      * The upvar is not in the current trace, so get the upvar value exactly as
      * the interpreter does and unbox.
      */
     uint32 level = script->staticLevel - cookie.level();
     uint32 cookieSlot = cookie.slot();
-    JSStackFrame* fp = cx->display[level];
+    JSStackFrame* fp = FindFrameAtLevel(cx, level);
     const CallInfo* ci;
     int32 slot;
     if (!fp->fun || (fp->flags & JSFRAME_EVAL)) {
         ci = &GetUpvarStackOnTrace_ci;
         slot = cookieSlot;
     } else if (cookieSlot < fp->fun->nargs) {
         ci = &GetUpvarArgOnTrace_ci;
         slot = cookieSlot;
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1449,42 +1449,29 @@ mjit::Compiler::jsop_getglobal(uint32 in
     Address address = masm.objSlotRef(globalObj, reg, slot);
     frame.freeReg(reg);
     frame.push(address);
 }
 
 void
 mjit::Compiler::emitReturn()
 {
-    RegisterID t0 = frame.allocReg();
-
     /*
      * if (!f.inlineCallCount)
      *     return;
      */
     Jump noInlineCalls = masm.branchPtr(Assembler::Equal,
                                         FrameAddress(offsetof(VMFrame, inlineCallCount)),
                                         ImmPtr(0));
     stubcc.linkExit(noInlineCalls, Uses(frame.frameDepth()));
 #if defined(JS_CPU_ARM)
     stubcc.masm.loadPtr(FrameAddress(offsetof(VMFrame, scriptedReturn)), JSC::ARMRegisters::lr);
 #endif
     stubcc.masm.ret();
 
-    /* Restore display. */
-    if (script->staticLevel < JS_DISPLAY_SIZE) {
-        RegisterID t1 = frame.allocReg();
-        masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), t0);
-        masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, displaySave)), t1);
-        masm.storePtr(t1, Address(t0,
-                                  offsetof(JSContext, display) +
-                                  script->staticLevel * sizeof(JSStackFrame*)));
-        frame.freeReg(t1);
-    }
-
     JS_ASSERT_IF(!fun, JSOp(*PC) == JSOP_STOP);
 
     /*
      * If there's a function object, deal with the fact that it can escape.
      * Note that after we've placed the call object, all tracked state can
      * be thrown away. This will happen anyway because the next live opcode
      * (if any) must have an incoming edge.
      *
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -237,22 +237,16 @@ CreateFrame(VMFrame &f, uint32 flags, ui
                                cx->debugHooks->callHookData);
         // CHECK_INTERRUPT_HANDLER();
     } else {
         newfp->hookData = NULL;
     }
 
     stack.pushInlineFrame(cx, fp, cx->regs->pc, newfp);
 
-    if (newscript->staticLevel < JS_DISPLAY_SIZE) {
-        JSStackFrame **disp = &cx->display[newscript->staticLevel];
-        newfp->displaySave = *disp;
-        *disp = newfp;
-    }
-
     return true;
 }
 
 static inline void
 FixVMFrame(VMFrame &f, JSStackFrame *fp)
 {
     f.inlineCallCount++;
     f.fp->ncode = f.scriptedReturn;
@@ -295,19 +289,16 @@ InlineCall(VMFrame &f, uint32 flags, voi
 static bool
 InlineReturn(JSContext *cx, JSBool ok)
 {
     JSStackFrame *fp = cx->fp;
 
     JS_ASSERT(!fp->blockChain);
     JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
 
-    if (fp->script->staticLevel < JS_DISPLAY_SIZE)
-        cx->display[fp->script->staticLevel] = fp->displaySave;
-
     // Marker for debug support.
     void *hookData = fp->hookData;
     if (JS_UNLIKELY(hookData != NULL)) {
         JSInterpreterHook hook;
         JSBool status;
 
         hook = cx->debugHooks->callHook;
         if (hook) {
@@ -489,22 +480,16 @@ CreateLightFrame(VMFrame &f, uint32 flag
 
 #ifdef DEBUG
     newfp->savedPC = JSStackFrame::sInvalidPC;
 #endif
     newfp->down = fp;
     fp->savedPC = f.regs.pc;
     cx->setCurrentFrame(newfp);
 
-    if (newscript->staticLevel < JS_DISPLAY_SIZE) {
-        JSStackFrame **disp = &cx->display[newscript->staticLevel];
-        newfp->displaySave = *disp;
-        *disp = newfp;
-    }
-
     return true;
 }
 
 /*
  * stubs::Call is guaranteed to be called on a scripted call with JIT'd code.
  */
 void * JS_FASTCALL
 stubs::Call(VMFrame &f, uint32 argc)
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1443,17 +1443,17 @@ stubs::InitElem(VMFrame &f, uint32 last)
 
 void JS_FASTCALL
 stubs::GetUpvar(VMFrame &f, uint32 ck)
 {
     /* :FIXME: We can do better, this stub isn't needed. */
     uint32 staticLevel = f.fp->script->staticLevel;
     UpvarCookie cookie;
     cookie.fromInteger(ck);
-    f.regs.sp[0] = js_GetUpvar(f.cx, staticLevel, cookie);
+    f.regs.sp[0] = GetUpvar(f.cx, staticLevel, cookie);
 }
 
 JSObject * JS_FASTCALL
 stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
 {
     /*
      * Define a local function (i.e., one nested at the top level of another
      * function), parented by the current scope chain, stored in a local