Rewrite and cleanup GETELEM and SETELEM (455748, r=brendan).
authorAndreas Gal <gal@mozilla.com>
Thu, 25 Sep 2008 11:31:40 -0700
changeset 19979 34c61673341a7bf2dcd21fb6791bd10acbf99e97
parent 19977 15da264a371acb1b47aaac74b1da31f925bc3c00
child 19980 3d304a99805eb25ee7e8a408f204b3f7e2e94f26
push id2577
push userbrendan@mozilla.com
push dateWed, 01 Oct 2008 04:35:27 +0000
treeherdermozilla-central@a613924403d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs455748
milestone1.9.1b1pre
Rewrite and cleanup GETELEM and SETELEM (455748, r=brendan).
js/src/builtins.tbl
js/src/jsbuiltins.cpp
js/src/jstracer.cpp
js/src/jstracer.h
js/src/trace-test.js
--- a/js/src/builtins.tbl
+++ b/js/src/builtins.tbl
@@ -108,20 +108,18 @@ BUILTIN2(EqualStrings,          LO,     
 BUILTIN2(CompareStrings,        LO,     LO, LO, bool,      JSString*, JSString*, 1, 1)
 BUILTIN2(StringToNumber,        LO,     LO, F,  jsdouble,  JSContext*, JSString*, 1, 1)
 BUILTIN2(StringToInt32,         LO,     LO, LO, jsint,     JSContext*, JSString*, 1, 1)
 BUILTIN2(ParseInt,              LO,     LO, F,  jsdouble,  JSContext*, JSString*, 1, 1)
 BUILTIN1(ParseIntDouble,        F,          F,  jsdouble,  jsdouble, 1, 1)
 BUILTIN2(ParseFloat,            LO,     LO, F,  jsdouble,  JSContext*, JSString*, 1, 1)
 BUILTIN3(Any_getprop,           LO, LO, LO, P,  jsval,     JSContext*, JSObject*, JSString*, 0, 0)
 BUILTIN4(Any_setprop,           LO, LO, LO, LO, LO, bool,  JSContext*, JSObject*, JSString*, jsval, 0, 0)
-BUILTIN3(Any_getelem,           LO, LO, F,  P,  jsval,     JSContext*, JSObject*, jsdouble, 0, 0)
-BUILTIN4(Any_setelem,           LO, LO, F,  LO, LO, bool,  JSContext*, JSObject*, jsdouble, jsval, 0, 0)
-BUILTIN3(Any_getelem_int,       LO, LO, LO, P,  jsval,     JSContext*, JSObject*, jsuint, 0, 0)
-BUILTIN4(Any_setelem_int,       LO, LO, LO, LO, LO, bool,  JSContext*, JSObject*, jsuint, jsval, 0, 0)
+BUILTIN3(Any_getelem,           LO, LO, LO, P,  jsval,     JSContext*, JSObject*, jsuint, 0, 0)
+BUILTIN4(Any_setelem,           LO, LO, LO, LO, LO, bool,  JSContext*, JSObject*, jsuint, jsval, 0, 0)
 BUILTIN3(FastValueToIterator,   LO, LO, LO, P,  JSObject*, JSContext*, jsuint, jsval, 0, 0)
 BUILTIN2(FastCallIteratorNext,  LO,     LO, P,  JSObject*, JSContext*, JSObject*, 0, 0)
 BUILTIN2(CloseIterator,         LO,     LO, LO, bool,      JSContext*, jsval, 0, 0)
 BUILTIN2(CallTree,              LO, LO, P,      nanojit::GuardRecord*, avmplus::InterpState*, nanojit::Fragment*, 0, 0)
 BUILTIN2(FastNewArray,          LO,     LO, P,  JSObject*, JSContext*, JSObject*, 0, 0)
 BUILTIN2(FastNewObject,         LO,     LO, P,  JSObject*, JSContext*, JSObject*, 0, 0)
 BUILTIN3(AddProperty,           LO, LO, LO, LO, bool,      JSContext*, JSObject*, JSScopeProperty*, 0, 0)
 BUILTIN3(HasNamedProperty,      LO, LO, LO, LO, bool,      JSContext*, JSObject*, JSString*, 0, 0)
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -508,54 +508,35 @@ js_Any_setprop(JSContext* cx, JSObject* 
 {
     jsid id;
     if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id))
         return JS_FALSE;
     return OBJ_SET_PROPERTY(cx, obj, id, &v);
 }
 
 jsval FASTCALL
-js_Any_getelem(JSContext* cx, JSObject* obj, jsdouble index)
+js_Any_getelem(JSContext* cx, JSObject* obj, jsint index)
 {
     jsval v;
     jsid id;
-
-    if (!js_ValueToStringId(cx, DOUBLE_TO_JSVAL(&index), &id))
-        return JSVAL_ERROR_COOKIE;
-    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
+    if (index < 0)
         return JSVAL_ERROR_COOKIE;
-    return v;
-}
-
-JSBool FASTCALL
-js_Any_setelem(JSContext* cx, JSObject* obj, jsdouble index, jsval v)
-{
-    jsid id;
-    if (!js_ValueToStringId(cx, DOUBLE_TO_JSVAL(&index), &id))
-        return JS_FALSE;
-    return OBJ_SET_PROPERTY(cx, obj, id, &v);
-}
-
-jsval FASTCALL
-js_Any_getelem_int(JSContext* cx, JSObject* obj, jsuint index)
-{
-    jsval v;
-    jsid id;
-
     if (!js_IndexToId(cx, index, &id))
         return JSVAL_ERROR_COOKIE;
     if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
         return JSVAL_ERROR_COOKIE;
     return v;
 }
 
 JSBool FASTCALL
-js_Any_setelem_int(JSContext* cx, JSObject* obj, jsuint index, jsval v)
+js_Any_setelem(JSContext* cx, JSObject* obj, jsint index, jsval v)
 {
     jsid id;
+    if (index < 0)
+        return JSVAL_ERROR_COOKIE;
     if (!js_IndexToId(cx, index, &id))
         return JS_FALSE;
     return OBJ_SET_PROPERTY(cx, obj, id, &v);
 }
 
 JSObject* FASTCALL
 js_FastValueToIterator(JSContext* cx, jsuint flags, jsval v)
 {
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -660,30 +660,16 @@ public:
             JS_ASSERT(s0->isQuad());
             if (s0->isop(LIR_i2f)) {
                 LIns* args2[] = { s0->oprnd1(), args[1] };
                 return out->insCall(F_BoxInt32, args2);
             }
             if (s0->isCall() && s0->fid() == F_UnboxDouble) 
                 return callArgN(s0, 0);
             break;
-          case F_Any_getelem:
-            JS_ASSERT(s0->isQuad());
-            if (isPromote(s0)) {
-                LIns* args2[] = { demote(out, s0), args[1], args[2] };
-                return out->insCall(F_Any_getelem_int, args2);
-            }
-            break;
-          case F_Any_setelem:
-            JS_ASSERT(args[1]->isQuad());
-            if (isPromote(args[1])) {
-                LIns* args2[] = { s0, demote(out, args[1]), args[2], args[3] };
-                return out->insCall(F_Any_setelem_int, args2);
-            }
-            break;
         }
         return out->insCall(fid, args);
     }
 };
 
 /* In debug mode vpname contains a textual description of the type of the
    slot during the forall iteration over al slots. */
 #if defined(DEBUG) || defined(INCLUDE_VERBOSE_OUTPUT)
@@ -3011,16 +2997,29 @@ TraceRecorder::stack(int n, LIns* i)
     set(&stackval(n), i, n >= 0);
 }
 
 LIns* TraceRecorder::f2i(LIns* f)
 {
     return lir->insCall(F_DoubleToInt32, &f);
 }
 
+LIns* TraceRecorder::makeNumberInt32(LIns* f)
+{
+    JS_ASSERT(f->isQuad());
+    LIns* x;
+    if (!isPromote(f)) {
+        x = f2i(f);
+        guard(true, lir->ins2(LIR_feq, f, lir->ins1(LIR_i2f, x)), MISMATCH_EXIT);
+    } else {
+        x = ::demote(lir, f);
+    }
+    return x;
+}
+
 bool
 TraceRecorder::ifop()
 {
     jsval& v = stackval(-1);
     LIns* v_ins = get(&v);
     /* no need to guard if condition is constant */
     if (v_ins->isconst() || v_ins->isconstq())
         return true;
@@ -3761,16 +3760,29 @@ TraceRecorder::guardClass(JSObject* obj,
     char namebuf[32];
     JS_snprintf(namebuf, sizeof namebuf, "guard(class is %s)", clasp->name);
     guard(true, addName(lir->ins2(LIR_eq, class_ins, INS_CONSTPTR(clasp)), namebuf),
           MISMATCH_EXIT);
     return true;
 }
 
 bool
+TraceRecorder::guardNotGlobalObject(JSObject* obj, LIns* obj_ins)
+{
+    /*
+     * Can't specialize to assert obj != global, must guard to avoid aliasing
+     * stale homes of stacked global variables.
+     */
+    if (obj == globalObj)
+        ABORT_TRACE("elem op aliases global");
+    guard(false, lir->ins2(LIR_eq, obj_ins, INS_CONSTPTR(globalObj)), MISMATCH_EXIT);
+    return true;
+}
+
+bool
 TraceRecorder::guardDenseArray(JSObject* obj, LIns* obj_ins)
 {
     return guardClass(obj, obj_ins, &js_ArrayClass);
 }
 
 bool
 TraceRecorder::guardDenseArrayIndex(JSObject* obj, jsint idx, LIns* obj_ins,
                                     LIns* dslots_ins, LIns* idx_ins)
@@ -4601,117 +4613,141 @@ TraceRecorder::record_SetPropMiss(JSProp
 #endif
 
     return record_SetPropHit(entry, sprop);
 }
 
 bool
 TraceRecorder::record_JSOP_GETELEM()
 {
-    jsval& r = stackval(-1);
-    jsval& l = stackval(-2);
-
-    if (JSVAL_IS_STRING(l) && JSVAL_IS_INT(r)) {
-        int i;
-
-        i = JSVAL_TO_INT(r);
-        if ((size_t)i >= JSSTRING_LENGTH(JSVAL_TO_STRING(l)))
+    jsval& idx = stackval(-1);
+    jsval& obj = stackval(-2);
+
+    LIns* obj_ins = get(&obj);
+    LIns* idx_ins = get(&idx);
+    
+    if (JSVAL_IS_STRING(obj) && JSVAL_IS_INT(idx)) {
+        int i = JSVAL_TO_INT(idx);
+        if ((size_t)i >= JSSTRING_LENGTH(JSVAL_TO_STRING(obj)))
             ABORT_TRACE("Invalid string index in JSOP_GETELEM");
-
-        LIns* args[] = { f2i(get(&r)), get(&l), cx_ins };
+        idx_ins = makeNumberInt32(idx_ins);
+        LIns* args[] = { idx_ins, obj_ins, cx_ins };
         LIns* unitstr_ins = lir->insCall(F_String_getelem, args);
         guard(false, lir->ins_eq0(unitstr_ins), MISMATCH_EXIT);
-        set(&l, unitstr_ins);
+        set(&obj, unitstr_ins);
         return true;
     }
 
-    if (!JSVAL_IS_PRIMITIVE(l) &&
-        (JSVAL_IS_STRING(r) || 
-         (isNumber(r) && (!JSVAL_IS_INT(r) || !OBJ_IS_DENSE_ARRAY(cx, JSVAL_TO_OBJECT(l)))))) {
-        jsval v;
-        jsid id;
-        uint32 fid;
-
-        if (JSVAL_IS_STRING(r)) {
-            if (!js_ValueToStringId(cx, r, &id))
-                return false;
-            r = ID_TO_VALUE(id);
-            fid = F_Any_getprop;
-        } else {
-            if (!js_IndexToId(cx, JSVAL_TO_INT(r), &id))
-                return false;
-            fid = F_Any_getelem;
-        }
-        if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(l), id, &v))
+    if (JSVAL_IS_PRIMITIVE(obj))
+        ABORT_TRACE("JSOP_GETLEM on a primitive");
+    
+    jsval id;
+    jsval v;
+    LIns* v_ins;
+    
+    /* Property access using a string name. */
+    if (JSVAL_IS_STRING(idx)) {
+        if (!guardNotGlobalObject(JSVAL_TO_OBJECT(obj), obj_ins))
             return false;
-
-        LIns* args[] = { get(&r), get(&l), cx_ins };
-        LIns* v_ins = lir->insCall(fid, args);
+        if (!js_ValueToStringId(cx, idx, &id))
+            return false;
+        if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(obj), id, &v))
+            return false;
+        LIns* args[] = { idx_ins, obj_ins, cx_ins };
+        v_ins = lir->insCall(F_Any_getprop, args);
         guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), MISMATCH_EXIT);
         if (!unbox_jsval(v, v_ins))
             ABORT_TRACE("JSOP_GETELEM");
-        set(&l, v_ins);
+        set(&obj, v_ins);
+        return true;
+    }
+    
+    /* At this point we expect a whole number or we bail. */
+    if (!JSVAL_IS_INT(idx))
+        ABORT_TRACE("non-string, non-int JSOP_GETELEM index");
+
+    /* Accessing an object using integer index but not a dense array. */
+    if (!OBJ_IS_DENSE_ARRAY(cx, JSVAL_TO_OBJECT(obj))) {
+        idx_ins = makeNumberInt32(idx_ins);
+        if (!js_IndexToId(cx, JSVAL_TO_INT(idx), &id))
+            return false;
+        if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(obj), id, &v))
+            return false;
+        LIns* args[] = { idx_ins, obj_ins, cx_ins };
+        LIns* v_ins = lir->insCall(F_Any_getelem, args);
+        guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), MISMATCH_EXIT);
+        if (!unbox_jsval(v, v_ins))
+            ABORT_TRACE("JSOP_GETELEM");
+        set(&obj, v_ins);
         return true;
     }
 
     jsval* vp;
-    LIns* v_ins;
     LIns* addr_ins;
-    if (!elem(l, r, vp, v_ins, addr_ins))
+    if (!elem(obj, idx, vp, v_ins, addr_ins))
         return false;
-    set(&l, v_ins);
+    set(&obj, v_ins);
     return true;
 }
 
 bool
 TraceRecorder::record_JSOP_SETELEM()
 {
     jsval& v = stackval(-1);
-    jsval& r = stackval(-2);
-    jsval& l = stackval(-3);
+    jsval& idx = stackval(-2);
+    jsval& obj = stackval(-3);
 
     /* no guards for type checks, trace specialized this already */
-    if (JSVAL_IS_PRIMITIVE(l))
+    if (JSVAL_IS_PRIMITIVE(obj))
         ABORT_TRACE("left JSOP_SETELEM operand is not an object");
-    JSObject* obj = JSVAL_TO_OBJECT(l);
-    LIns* obj_ins = get(&l);
-
-    if (JSVAL_IS_STRING(r) || 
-        (isNumber(r) && (!JSVAL_IS_INT(r) || !guardDenseArray(obj, obj_ins)))) {
-        LIns* v_ins = get(&v);
+
+    LIns* obj_ins = get(&obj);
+    LIns* idx_ins = get(&idx);
+    LIns* v_ins = get(&v);
+    
+    if (JSVAL_IS_STRING(idx)) {
+        if (!guardNotGlobalObject(JSVAL_TO_OBJECT(obj), obj_ins))
+            return false;
         LIns* unboxed_v_ins = v_ins;
         if (!box_jsval(v, v_ins))
             ABORT_TRACE("boxing string-indexed JSOP_SETELEM value");
-        LIns* args[] = { v_ins, get(&r), get(&l), cx_ins };
-        LIns* ok_ins = lir->insCall(JSVAL_IS_STRING(r) ? F_Any_setprop : F_Any_setelem, args);
+        LIns* args[] = { v_ins, idx_ins, obj_ins, cx_ins };
+        LIns* ok_ins = lir->insCall(F_Any_setprop, args);
         guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT);
-        set(&l, unboxed_v_ins);
+        set(&obj, unboxed_v_ins);
         return true;
     }
-    if (!JSVAL_IS_INT(r))
+
+    if (!JSVAL_IS_INT(idx))
         ABORT_TRACE("non-string, non-int JSOP_SETELEM index");
 
-    /* check that the index is within bounds */
-    LIns* idx_ins = f2i(get(&r));
-
-    /* we have to check that its really an integer, but this check will to go away
-       once we peel the loop type down to integer for this slot */
-    guard(true, lir->ins2(LIR_feq, get(&r), lir->ins1(LIR_i2f, idx_ins)), MISMATCH_EXIT);
+    idx_ins = makeNumberInt32(idx_ins);
+    
+    if (!guardDenseArray(JSVAL_TO_OBJECT(obj), obj_ins)) {
+        LIns* unboxed_v_ins = v_ins;
+        if (!box_jsval(v, v_ins))
+            ABORT_TRACE("boxing string-indexed JSOP_SETELEM value");
+        LIns* args[] = { v_ins, idx_ins, obj_ins, cx_ins };
+        LIns* ok_ins = lir->insCall(F_Any_setelem, args);
+        guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT);
+        set(&obj, unboxed_v_ins);
+        return true;
+    }
+
     /* ok, box the value we are storing, store it and we are done */
-    LIns* v_ins = get(&v);
     LIns* boxed_ins = v_ins;
     if (!box_jsval(v, boxed_ins))
         ABORT_TRACE("boxing failed");
     LIns* args[] = { boxed_ins, idx_ins, obj_ins, cx_ins };
     LIns* res_ins = lir->insCall(F_Array_dense_setelem, args);
     guard(false, lir->ins_eq0(res_ins), MISMATCH_EXIT);
 
     jsbytecode* pc = cx->fp->regs->pc;
     if (*pc == JSOP_SETELEM && pc[JSOP_SETELEM_LENGTH] != JSOP_POP)
-        set(&l, v_ins);
+        set(&obj, v_ins);
     return true;
 }
 
 bool
 TraceRecorder::record_JSOP_CALLNAME()
 {
     JSObject* obj = cx->fp->scopeChain;
     if (obj != globalObj) {
@@ -5247,48 +5283,39 @@ TraceRecorder::prop(JSObject* obj, LIns*
 
     v_ins = stobj_get_slot(obj_ins, slot, dslots_ins);
     if (!unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins))
         ABORT_TRACE("unboxing");
     return true;
 }
 
 bool
-TraceRecorder::elem(jsval& l, jsval& r, jsval*& vp, LIns*& v_ins, LIns*& addr_ins)
+TraceRecorder::elem(jsval& obj, jsval& idx, jsval*& vp, LIns*& v_ins, LIns*& addr_ins)
 {
     /* no guards for type checks, trace specialized this already */
-    if (JSVAL_IS_PRIMITIVE(l) || !JSVAL_IS_INT(r))
+    if (JSVAL_IS_PRIMITIVE(obj) || !JSVAL_IS_INT(idx))
         return false;
 
-    /*
-     * Can't specialize to assert obj != global, must guard to avoid aliasing
-     * stale homes of stacked global variables.
-     */
-    JSObject* obj = JSVAL_TO_OBJECT(l);
-    if (obj == globalObj)
-        ABORT_TRACE("elem op aliases global");
-    LIns* obj_ins = get(&l);
-    guard(false, lir->ins2(LIR_eq, obj_ins, INS_CONSTPTR(globalObj)), MISMATCH_EXIT);
+    LIns* obj_ins = get(&obj);
+    
+    if (!guardNotGlobalObject(JSVAL_TO_OBJECT(obj), obj_ins))
+        return false;
 
     /* make sure the object is actually a dense array */
-    if (!guardDenseArray(obj, obj_ins))
+    if (!guardDenseArray(JSVAL_TO_OBJECT(obj), obj_ins))
         return false;
 
     /* check that the index is within bounds */
-    jsint idx = JSVAL_TO_INT(r);
-    LIns* idx_ins = f2i(get(&r));
-
-    /* we have to check that its really an integer, but this check will to go away
-       once we peel the loop type down to integer for this slot */
-    guard(true, lir->ins2(LIR_feq, get(&r), lir->ins1(LIR_i2f, idx_ins)), MISMATCH_EXIT);
+    jsint i = JSVAL_TO_INT(idx);
+    LIns* idx_ins = makeNumberInt32(get(&idx));
 
     LIns* dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots));
-    if (!guardDenseArrayIndex(obj, idx, obj_ins, dslots_ins, idx_ins))
+    if (!guardDenseArrayIndex(JSVAL_TO_OBJECT(obj), i, obj_ins, dslots_ins, idx_ins))
         return false;
-    vp = &obj->dslots[idx];
+    vp = &JSVAL_TO_OBJECT(obj)->dslots[i];
 
     addr_ins = lir->ins2(LIR_piadd, dslots_ins,
                          lir->ins2i(LIR_pilsh, idx_ins, (sizeof(jsval) == 4) ? 2 : 3));
 
     /* load the value, check the type (need to check JSVAL_HOLE only for booleans) */
     v_ins = lir->insLoad(LIR_ldp, addr_ins, 0);
     if (!unbox_jsval(*vp, v_ins))
         return false;
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -271,17 +271,18 @@ class TraceRecorder : public GCObject {
     nanojit::LIns* arg(unsigned n);
     void arg(unsigned n, nanojit::LIns* i);
     nanojit::LIns* var(unsigned n);
     void var(unsigned n, nanojit::LIns* i);
     nanojit::LIns* stack(int n);
     void stack(int n, nanojit::LIns* i);
 
     nanojit::LIns* f2i(nanojit::LIns* f);
-
+    nanojit::LIns* makeNumberInt32(nanojit::LIns* f);
+    
     bool ifop();
     bool switchop();
     bool inc(jsval& v, jsint incr, bool pre = true);
     bool inc(jsval& v, nanojit::LIns*& v_ins, jsint incr, bool pre = true);
     bool incProp(jsint incr, bool pre = true);
     bool incElem(jsint incr, bool pre = true);
     bool incName(jsint incr, bool pre = true);
 
@@ -308,25 +309,26 @@ class TraceRecorder : public GCObject {
                                   nanojit::LIns*& dslots_ins);
     bool native_set(nanojit::LIns* obj_ins, JSScopeProperty* sprop,
                     nanojit::LIns*& dslots_ins, nanojit::LIns* v_ins);
     bool native_get(nanojit::LIns* obj_ins, nanojit::LIns* pobj_ins, JSScopeProperty* sprop,
                     nanojit::LIns*& dslots_ins, nanojit::LIns*& v_ins);
     
     bool name(jsval*& vp);
     bool prop(JSObject* obj, nanojit::LIns* obj_ins, uint32& slot, nanojit::LIns*& v_ins);
-    bool elem(jsval& l, jsval& r, jsval*& vp, nanojit::LIns*& v_ins, nanojit::LIns*& addr_ins);
+    bool elem(jsval& obj, jsval& idx, jsval*& vp, nanojit::LIns*& v_ins, nanojit::LIns*& addr_ins);
 
     bool getProp(JSObject* obj, nanojit::LIns* obj_ins);
     bool getProp(jsval& v);
     bool getThis(nanojit::LIns*& this_ins);
     
     bool box_jsval(jsval v, nanojit::LIns*& v_ins);
     bool unbox_jsval(jsval v, nanojit::LIns*& v_ins);
     bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp);
+    bool guardNotGlobalObject(JSObject* obj, nanojit::LIns* obj_ins);
     bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins);
     bool guardDenseArrayIndex(JSObject* obj, jsint idx, nanojit::LIns* obj_ins,
                               nanojit::LIns* dslots_ins, nanojit::LIns* idx_ins);
     void clearFrameSlotsFromCache();
     bool guardShapelessCallee(jsval& callee);
     bool interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc, bool constructing);
     bool forInLoop(jsval* vp);
 
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -1591,11 +1591,20 @@ function testInnerSwitchBreak()
         r[i] = innerSwitch(0);
     }
 
     return r.join(",");
 }
 testInnerSwitchBreak.expected = "1,1,1,1,1";
 test(testInnerSwitchBreak);
 
+function testArrayNaNIndex() 
+{
+    for (var j = 0; j < 4; ++j) { [this[NaN]]; }
+    for (var j = 0; j < 5; ++j) { if([1][-0]) { } }
+    return "ok";
+}
+testArrayNaNIndex.expected = "ok";
+test(testArrayNaNIndex);
+
 /* Keep these at the end so that we can see the summary after the trace-debug spew. */
 print("\npassed:", passes.length && passes.join(","));
 print("\nFAILED:", fails.length && fails.join(","));