Merge.
authorAndreas Gal <gal@mozilla.com>
Fri, 07 Nov 2008 18:19:52 -0800
changeset 21527 85f93f1826a988783042a60024bdd17519b3d3be
parent 21525 c7526e1cad3a7cb97601751d083ef627d0daf2dc (current diff)
parent 21526 1b4453d693d8148dfbd337ffb801f03349b8793b (diff)
child 21528 17747605490ced68f58d9b0cd1a542fdb4b33d3e
push id3558
push userrsayre@mozilla.com
push dateSat, 08 Nov 2008 09:06:43 +0000
treeherdermozilla-central@17747605490c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.1b2pre
Merge.
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -4756,33 +4756,33 @@ js_Interpret(JSContext *cx)
                 goto do_call;
 
             bool apply = (JSFastNative)fun->u.n.native == js_fun_apply;
             if (!apply && (JSFastNative)fun->u.n.native != js_fun_call)
                 goto do_call;
 
             /* 
              * If this is apply, and the argument is too long and would need
-             * a separate stack chunk, deoptimize and go the js_Invoke route.
+             * a separate stack chunk, do a heavy-weight apply. 
              */
             if (apply && argc >= 2 && !JSVAL_IS_PRIMITIVE(vp[3])) {
                 /* 
                  * This is a problem only if the second argument is 
                  * array-like. 
                  */
                 JSBool arraylike = JS_FALSE;
                 jsuint length = 0;
                 JSObject* aobj = JSVAL_TO_OBJECT(vp[3]);
                 if (js_IsArrayLike(cx, aobj, &arraylike, &length)) {
                     length = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1);
                     jsval* newsp = vp + 2 + length;
                     JS_ASSERT(newsp >= vp + 2);
                     JSArena *a = cx->stackPool.current;
                     if (jsuword(newsp) > a->limit)
-                        goto do_call; 
+                        goto do_call; /* do a heavy-weight apply */
                 }
             }
             
             obj = JS_THIS_OBJECT(cx, vp);
             if (!obj || !OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &vp[1]))
                 goto error;
             rval = vp[1];
 
@@ -4899,39 +4899,36 @@ js_Interpret(JSContext *cx)
                                  * state with undefineds for all arguments we 
                                  * didn't unpack yet, so we leave it at that. 
                                  */
                                 goto error;
                             }
                             sp++;
                         }
                         
-                        TRACE_1(ApplyComplete, argc);
-                        goto do_call_with_argc;
+                        goto do_call_with_specified_vp_and_argc;
                     }
-                } else {
+                } else
                     argc = 0;
-                }
             }
                  
             vp[0] = rval;
             vp[1] = OBJECT_TO_JSVAL(obj);
             regs.sp = vp + 2 + argc;
 
-            TRACE_1(ApplyComplete, argc);
-            goto do_call_with_argc;
+            goto do_call_with_specified_vp_and_argc;
           }
           
           BEGIN_CASE(JSOP_CALL)
           BEGIN_CASE(JSOP_EVAL)
           do_call:
             argc = GET_ARGC(regs.pc);
-
-          do_call_with_argc:
             vp = regs.sp - (argc + 2);
+            
+          do_call_with_specified_vp_and_argc:
             lval = *vp;
             if (VALUE_IS_FUNCTION(cx, lval)) {
                 obj = JSVAL_TO_OBJECT(lval);
                 fun = GET_FUNCTION_PRIVATE(cx, obj);
 
                 /* Clear frame flags since this is not a constructor call. */
                 flags = 0;
                 if (FUN_INTERPRETED(fun))
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -990,16 +990,17 @@ TraceRecorder::TraceRecorder(JSContext* 
     this->globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
     this->anchor = _anchor;
     this->fragment = _fragment;
     this->lirbuf = _fragment->lirbuf;
     this->treeInfo = ti;
     this->callDepth = _anchor ? _anchor->calldepth : 0;
     this->atoms = cx->fp->script->atomMap.vector;
     this->deepAborted = false;
+    this->applyingArguments = false;
     this->trashTree = false;
     this->whichTreeToTrash = _fragment->root;
     this->global_dslots = this->globalObj->dslots;
     this->terminate = false;
     this->outerToBlacklist = outerToBlacklist;
 
     debug_only_v(printf("recording starting from %s:%u@%u\n", cx->fp->script->filename,
                         js_PCToLineNumber(cx, cx->fp->script, cx->fp->regs->pc),
@@ -3587,18 +3588,19 @@ js_MonitorRecording(TraceRecorder* tr)
         js_AbortRecording(cx, "no more LIR memory");
         js_FlushJITCache(cx);
         return false;
     }
 
     if (tr->walkedOutOfLoop())
         return js_CloseLoop(cx);
 
-    // Clear one-shot state used to communicate between record_JSOP_CALL and post-                                                                                            
-    // opcode-case-guts record hook (record_FastNativeCallComplete).
+    // Clear one-shot state used to communicate between record_JSOP_CALL and mid- and post-
+    // opcode-case-guts record hooks (record_EnterFrame, record_FastNativeCallComplete).
+    tr->applyingArguments = false;
     tr->pendingTraceableNative = NULL;
 
     // In the future, handle dslots realloc by computing an offset from dslots instead.
     if (tr->global_dslots != tr->globalObj->dslots) {
         js_AbortRecording(cx, "globalObj->dslots reallocated");
         return false;
     }
 
@@ -4737,17 +4739,16 @@ TraceRecorder::unbox_jsval(jsval v, LIns
         guard(true,
               lir->ins2i(LIR_eq,
                         lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
                         JSVAL_STRING),
               MISMATCH_EXIT);
         v_ins = lir->ins2(LIR_piand, v_ins, INS_CONST(~JSVAL_TAGMASK));
         return true;
     }
-    JS_NOT_REACHED("unbox_jsval");
     return false;
 }
 
 bool
 TraceRecorder::getThis(LIns*& this_ins)
 {
     if (cx->fp->callee) { /* in a function */
         if (JSVAL_IS_NULL(cx->fp->argv[-1]))
@@ -4922,20 +4923,30 @@ TraceRecorder::record_EnterFrame()
     
     debug_only_v(printf("EnterFrame %s, callDepth=%d\n",
                         js_AtomToPrintableString(cx, cx->fp->fun->atom),
                         callDepth););
     LIns* void_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
 
     jsval* vp = &fp->argv[fp->argc];
     jsval* vpstop = vp + ptrdiff_t(fp->fun->nargs) - ptrdiff_t(fp->argc);
-    while (vp < vpstop) {
-        if (vp >= fp->down->regs->sp)
+    if (applyingArguments) {
+        applyingArguments = false;
+        while (vp < vpstop) {
+            JS_ASSERT(vp >= fp->down->regs->sp);
             nativeFrameTracker.set(vp, (LIns*)0);
-        set(vp++, void_ins, true);
+            LIns* arg_ins = get(&fp->down->argv[fp->argc + (vp - vpstop)]);
+            set(vp++, arg_ins, true);
+        }
+    } else {
+        while (vp < vpstop) {
+            if (vp >= fp->down->regs->sp)
+                nativeFrameTracker.set(vp, (LIns*)0);
+            set(vp++, void_ins, true);
+        }
     }
 
     vp = &fp->slots[0];
     vpstop = vp + fp->script->nfixed;
     while (vp < vpstop)
         set(vp++, void_ins, true);
     return true;
 }
@@ -5261,32 +5272,36 @@ JSBool
 js_Array(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
 
 JSBool
 js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
 
 JSBool
 js_Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
 
-bool
-TraceRecorder::functionCall(bool constructing, uintN argc)
+JSBool
+js_fun_apply(JSContext* cx, uintN argc, jsval* vp);
+
+bool
+TraceRecorder::functionCall(bool constructing)
 {
     JSStackFrame* fp = cx->fp;
     jsbytecode *pc = fp->regs->pc;
+    uintN argc = GET_ARGC(pc);
 
     jsval& fval = stackval(0 - (2 + argc));
     JS_ASSERT(&fval >= StackBase(fp));
 
     if (!VALUE_IS_FUNCTION(cx, fval))
         ABORT_TRACE("callee is not a function");
 
     jsval& tval = stackval(0 - (argc + 1));
     LIns* this_ins = get(&tval);
 
-    if (this_ins->isconstp() && !this_ins->constvalp() && !guardCallee(fval))
+    if (this_ins->isconstp() && !this_ins->constvalp() && !guardShapelessCallee(fval))
         return false;
 
     /*
      * Require that the callee be a function object, to avoid guarding on its
      * class here. We know if the callee and this were pushed by JSOP_CALLNAME
      * or JSOP_CALLPROP that callee is a *particular* function, since these hit
      * the property cache and guard on the object (this) in which the callee
      * was found. So it's sufficient to test here that the particular function
@@ -5302,16 +5317,97 @@ TraceRecorder::functionCall(bool constru
             LIns* args[] = { get(&fval), cx_ins };
             LIns* tv_ins = lir->insCall(&js_FastNewObject_ci, args);
             guard(false, lir->ins_eq0(tv_ins), OOM_EXIT);
             set(&tval, tv_ins);
         }
         return interpretedFunctionCall(fval, fun, argc, constructing);
     }
 
+    LIns* arg1_ins = NULL;
+    jsval arg1 = JSVAL_VOID;
+    jsval thisval = tval;
+    if (!constructing && FUN_FAST_NATIVE(fun) == js_fun_apply) {
+        if (argc != 2)
+            ABORT_TRACE("can't trace Function.prototype.apply with other than 2 args");
+
+        if (!guardShapelessCallee(tval))
+            return false;
+        JSObject* tfunobj = JSVAL_TO_OBJECT(tval);
+        JSFunction* tfun = GET_FUNCTION_PRIVATE(cx, tfunobj);
+
+        jsval& oval = stackval(-2);
+        if (JSVAL_IS_PRIMITIVE(oval))
+            ABORT_TRACE("can't trace Function.prototype.apply with primitive 1st arg");
+
+        jsval& aval = stackval(-1);
+        if (JSVAL_IS_PRIMITIVE(aval))
+            ABORT_TRACE("can't trace Function.prototype.apply with primitive 2nd arg");
+        JSObject* aobj = JSVAL_TO_OBJECT(aval);
+
+        LIns* aval_ins = get(&aval);
+        if (!aval_ins->isCall())
+            ABORT_TRACE("can't trace Function.prototype.apply on non-builtin-call 2nd arg");
+
+        if (aval_ins->callInfo() == &js_Arguments_ci) {
+            JS_ASSERT(OBJ_GET_CLASS(cx, aobj) == &js_ArgumentsClass);
+            JS_ASSERT(OBJ_GET_PRIVATE(cx, aobj) == fp);
+            if (!FUN_INTERPRETED(tfun))
+                ABORT_TRACE("can't trace Function.prototype.apply(native_function, arguments)");
+
+            // We can only fasttrack applys where the argument array we pass in has the
+            // same length (fp->argc) as the number of arguments the function expects (tfun->nargs).
+            argc = fp->argc;
+            if (tfun->nargs != argc || fp->fun->nargs != argc)
+                ABORT_TRACE("can't trace Function.prototype.apply(scripted_function, arguments)");
+
+            jsval* sp = fp->regs->sp - 4;
+            set(sp, get(&tval));
+            *sp++ = tval;
+            set(sp, get(&oval));
+            *sp++ = oval;
+            jsval* newsp = sp + argc;
+            if (newsp > fp->slots + fp->script->nslots) {
+                JSArena* a = cx->stackPool.current;
+                if (jsuword(newsp) > a->limit)
+                    ABORT_TRACE("can't grow stack for Function.prototype.apply");
+                if (jsuword(newsp) > a->avail)
+                    a->avail = jsuword(newsp);
+            }
+
+            jsval* argv = fp->argv;
+            for (uintN i = 0; i < JS_MIN(argc, 2); i++) {
+                set(&sp[i], get(&argv[i]));
+                sp[i] = argv[i];
+            }
+            applyingArguments = true;
+            return interpretedFunctionCall(tval, tfun, argc, false);
+        }
+
+        if (aval_ins->callInfo() != &js_Array_1str_ci)
+            ABORT_TRACE("can't trace Function.prototype.apply on other than [str] 2nd arg");
+
+        JS_ASSERT(OBJ_IS_ARRAY(cx, aobj));
+        JS_ASSERT(aobj->fslots[JSSLOT_ARRAY_LENGTH] == 1);
+        JS_ASSERT(JSVAL_IS_STRING(aobj->dslots[0]));
+
+        if (FUN_INTERPRETED(tfun))
+            ABORT_TRACE("can't trace Function.prototype.apply for scripted functions");
+
+        if (!(tfun->flags & JSFUN_TRACEABLE))
+            ABORT_TRACE("Function.prototype.apply on untraceable native");
+
+        thisval = oval;
+        this_ins = get(&oval);
+        arg1_ins = callArgN(aval_ins, 2);
+        arg1 = aobj->dslots[0];
+        fun = tfun;
+        argc = 1;
+    }
+
     if (!constructing && !(fun->flags & JSFUN_TRACEABLE))
         ABORT_TRACE("untraceable native");
 
     static JSTraceableNative knownNatives[] = {
         { (JSFastNative)js_Array,  &js_FastNewArray_ci,   "pC", "",    FAIL_NULL | JSTN_MORE },
         { (JSFastNative)js_Array,  &js_Array_1int_ci,     "pC", "i",   FAIL_NULL | JSTN_MORE },
         { (JSFastNative)js_Array,  &js_Array_2obj_ci,     "pC", "oo",  FAIL_NULL | JSTN_MORE },
         { (JSFastNative)js_Array,  &js_Array_3num_ci,     "pC", "ddd", FAIL_NULL | JSTN_MORE },
@@ -5339,21 +5435,21 @@ TraceRecorder::functionCall(bool constru
 #endif
 
         uintN i;
         for (i = prefixc; i--; ) {
             argtype = known->prefix[i];
             if (argtype == 'C') {
                 *argp = cx_ins;
             } else if (argtype == 'T') {   /* this, as an object */
-                if (!JSVAL_IS_OBJECT(tval))
+                if (!JSVAL_IS_OBJECT(thisval))
                     goto next_specialization;
                 *argp = this_ins;
             } else if (argtype == 'S') {   /* this, as a string */
-                if (!JSVAL_IS_STRING(tval))
+                if (!JSVAL_IS_STRING(thisval))
                     goto next_specialization;
                 *argp = this_ins;
             } else if (argtype == 'f') {
                 *argp = INS_CONSTPTR(JSVAL_TO_OBJECT(fval));
             } else if (argtype == 'p') {
                 JSObject* ctor = JSVAL_TO_OBJECT(fval);
                 jsval pval;
                 if (!OBJ_GET_PROPERTY(cx, ctor,
@@ -5365,28 +5461,28 @@ TraceRecorder::functionCall(bool constru
                 if (!JSVAL_IS_OBJECT(pval))
                     ABORT_TRACE("got primitive prototype from constructor");
                 *argp = INS_CONSTPTR(JSVAL_TO_OBJECT(pval));
             } else if (argtype == 'R') {
                 *argp = INS_CONSTPTR(cx->runtime);
             } else if (argtype == 'P') {
                 *argp = INS_CONSTPTR(pc);
             } else if (argtype == 'D') {  /* this, as a number */
-                if (!isNumber(tval))
+                if (!isNumber(thisval))
                     goto next_specialization;
                 *argp = this_ins;
             } else {
                 JS_NOT_REACHED("unknown prefix arg type");
             }
             argp--;
         }
 
         for (i = knownargc; i--; ) {
-            jsval& arg = stackval(-(i + 1));
-            *argp = get(&arg);
+            jsval& arg = (!constructing && i == 0 && arg1_ins) ? arg1 : stackval(-(i + 1));
+            *argp = (!constructing && i == 0 && arg1_ins) ? arg1_ins : get(&arg);
 
             argtype = known->argtypes[i];
             if (argtype == 'd' || argtype == 'i') {
                 if (!isNumber(arg))
                     goto next_specialization;
                 if (argtype == 'i')
                     *argp = f2i(*argp);
             } else if (argtype == 'o') {
@@ -5410,20 +5506,20 @@ TraceRecorder::functionCall(bool constru
             argp--;
         }
 
         /*
          * If we got this far, and we have a charCodeAt, check that charCodeAt
          * isn't going to return a NaN.
          */
         if (!constructing && known->builtin == &js_String_p_charCodeAt_ci) {
-            JSString* str = JSVAL_TO_STRING(tval);
-            jsval& arg = stackval(-1);
-
-            JS_ASSERT(JSVAL_IS_STRING(tval));
+            JSString* str = JSVAL_TO_STRING(thisval);
+            jsval& arg = arg1_ins ? arg1 : stackval(-1);
+
+            JS_ASSERT(JSVAL_IS_STRING(thisval));
             JS_ASSERT(isNumber(arg));
 
             if (JSVAL_IS_INT(arg)) {
                 if (size_t(JSVAL_TO_INT(arg)) >= JSSTRING_LENGTH(str))
                     ABORT_TRACE("invalid charCodeAt index");
             } else {
                 double d = js_DoubleToInteger(*JSVAL_TO_DOUBLE(arg));
                 if (d < 0 || JSSTRING_LENGTH(str) <= d)
@@ -5478,17 +5574,17 @@ success:
     }
 
     return true;
 }
 
 bool
 TraceRecorder::record_JSOP_NEW()
 {
-    return functionCall(true, GET_ARGC(cx->fp->regs->pc));
+    return functionCall(true);
 }
 
 bool
 TraceRecorder::record_JSOP_DELNAME()
 {
     return false;
 }
 
@@ -5898,24 +5994,21 @@ TraceRecorder::record_JSOP_GETUPVAR()
 
 bool
 TraceRecorder::record_JSOP_CALLUPVAR()
 {
     ABORT_TRACE("CALLUPVAR");
 }
 
 bool
-TraceRecorder::guardCallee(jsval& callee)
-{
-    if (!VALUE_IS_FUNCTION(cx, callee))
-        ABORT_TRACE("callee is not a function");
-    
+TraceRecorder::guardShapelessCallee(jsval& callee)
+{
     guard(true,
           addName(lir->ins2(LIR_eq, get(&callee), INS_CONSTPTR(JSVAL_TO_OBJECT(callee))),
-                  "guard(callee)"),
+                  "guard(shapeless callee)"),
           MISMATCH_EXIT);
     return true;
 }
 
 bool
 TraceRecorder::interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc, bool constructing)
 {
     if (JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(fval)) != globalObj)
@@ -5966,171 +6059,23 @@ TraceRecorder::interpretedFunctionCall(j
 
     atoms = fun->u.i.script->atomMap.vector;
     return true;
 }
 
 bool
 TraceRecorder::record_JSOP_CALL()
 {
-    return functionCall(false, GET_ARGC(cx->fp->regs->pc));
+    return functionCall(false);
 }
 
 bool
 TraceRecorder::record_JSOP_APPLY()
 {
-    JSStackFrame* fp = cx->fp;
-    jsbytecode *pc = fp->regs->pc;
-    uintN argc = GET_ARGC(pc);
-    jsval* vp = fp->regs->sp - (argc + 2);
-    JS_ASSERT(vp >= StackBase(fp));
-    jsuint length = 0;
-    JSObject* aobj = NULL;
-    LIns* aobj_ins = NULL;
-    LIns* dslots_ins = NULL;
-    
-    if (!VALUE_IS_FUNCTION(cx, vp[0]))
-        return record_JSOP_CALL();
-
-    JSObject* obj = JSVAL_TO_OBJECT(vp[0]);
-    JSFunction* fun = GET_FUNCTION_PRIVATE(cx, obj);
-    if (FUN_INTERPRETED(fun))
-        return record_JSOP_CALL();
-
-    bool apply = (JSFastNative)fun->u.n.native == js_fun_apply;
-    if (!apply && (JSFastNative)fun->u.n.native != js_fun_call)
-        return record_JSOP_CALL();
-
-    /* 
-     * If this is apply, and the argument is too long and would need
-     * a separate stack chunk, do a heavy-weight apply. 
-     */
-    if (apply && argc >= 2) {
-        if (JSVAL_IS_PRIMITIVE(vp[3]))
-            ABORT_TRACE("arguments parameter of apply is primitive");
-        aobj = JSVAL_TO_OBJECT(vp[3]);
-        aobj_ins = get(&vp[3]);
-        
-        /* 
-         * We expect a dense array for the arguments (the other
-         * frequent case is the arguments object, but that we
-         * don't trace at the moment). 
-         */
-        guard(false, lir->ins_eq0(aobj_ins), MISMATCH_EXIT);
-        if (!guardDenseArray(aobj, aobj_ins))
-            ABORT_TRACE("arguments parameter of apply is not a dense array");
-        
-        /*
-         * Make sure the array has the same length at runtime.
-         */
-        length = jsuint(aobj->fslots[JSSLOT_ARRAY_LENGTH]);
-        guard(true, 
-              lir->ins2i(LIR_eq,stobj_get_fslot(aobj_ins, JSSLOT_ARRAY_LENGTH), length), 
-              BRANCH_EXIT);
- 
-        /* 
-         * Make sure dslots is not NULL and guard the array's capacity.
-         */
-        dslots_ins = lir->insLoad(LIR_ldp, aobj_ins, offsetof(JSObject, dslots));
-        guard(false,
-              lir->ins_eq0(dslots_ins),
-              MISMATCH_EXIT);
-        guard(true,
-              lir->ins2(LIR_ult,
-                        lir->insImm(length),
-                        lir->insLoad(LIR_ldp, dslots_ins, 0 - (int)sizeof(jsval))),
-              MISMATCH_EXIT);
-        
-        /*
-         * If this is apply, and the argument is too long and would need
-         * a separate stack chunk, the interpreter deoptimizes and goes the 
-         * js_Invoke route. We can't trace that case.
-         */
-        length = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1);
-        jsval* newsp = vp + 2 + length;
-        JS_ASSERT(newsp >= vp + 2);
-        JSArena *a = cx->stackPool.current;
-        if (jsuword(newsp) > a->limit)
-            ABORT_TRACE("apply or call across stack-chunks");
-    }
-
-    /*
-     * Guard on the identity of this, which is the function we
-     * are applying.
-     */
-    if (!guardCallee(vp[1]))
-        return false;
-
-    LIns* callee_ins = get(&vp[1]);
-    LIns* this_ins = NULL;
-    if (argc > 0) {
-        this_ins = get(&vp[2]);
-        if (JSVAL_IS_PRIMITIVE(vp[2]))
-            ABORT_TRACE("apply with primitive this");
-    } else {
-        this_ins = lir->insImm(0);
-    }
-    
-    LIns** argv;
-    if (!apply) {
-        --argc;
-        argv = (LIns**)alloca(sizeof(LIns*) * argc);
-        for (jsuint n = 0; n < argc; ++n)
-            argv[n] = get(&vp[3 + n]); /* skip over the this parameter */
-    } else {
-        if (argc >= 2) {
-            /*
-             * We already established that argments is a dense array
-             * and we know its length and we know dslots is not NULL
-             * and the length is not beyond the dense array's capacity. 
-             */
-            argc = length;
-            argv = (LIns**)alloca(sizeof(LIns*) * argc);
-            for (unsigned n = 0; n < argc; ++n) {
-                /* Load the value and guard on its type to unbox it. */
-                LIns* v_ins = lir->insLoadi(dslots_ins, n * sizeof(jsval));
-                
-                /* 
-                 * We can't "see through" a hole to a possible Array.prototype property, so
-                 * we abort here and guard below (after unboxing).
-                 */
-                jsval* vp = &aobj->dslots[n];
-                if (*vp == JSVAL_HOLE)
-                    ABORT_TRACE("can't see through hole in dense array");
-                if (!unbox_jsval(*vp, v_ins))
-                    return false;
-                if (JSVAL_TAG(*vp) == JSVAL_BOOLEAN) {
-                    // Optimize to guard for a hole only after untagging, so we know that
-                    // we have a boolean, to avoid an extra guard for non-boolean values.
-                    guard(false, 
-                          lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_HOLE))),
-                          MISMATCH_EXIT);
-                }
-                argv[n] = v_ins;
-            }
-        } else {
-            argc = 0;
-        }
-    }
-    /*
-     * We have all arguments unpacked and we are now ready to modify the
-     * tracker.
-     */
-    tracker.set(&vp[0], callee_ins);
-    tracker.set(&vp[1], this_ins);
-    for (unsigned n = 0; n < argc; ++n)
-        tracker.set(&vp[2 + n], argv[n]);
-
-    return true;
-}
-
-bool
-TraceRecorder::record_ApplyComplete(uintN argc)
-{
-    return functionCall(false, argc);
+    return functionCall(false);
 }
 
 bool
 TraceRecorder::record_FastNativeCallComplete()
 {
     JS_ASSERT(pendingTraceableNative);
     
     /* At this point the generated code has already called the native function
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -278,16 +278,17 @@ class TraceRecorder : public avmplus::GC
 #endif
     nanojit::LIns*          cx_ins;
     nanojit::LIns*          gp_ins;
     nanojit::LIns*          eos_ins;
     nanojit::LIns*          eor_ins;
     nanojit::LIns*          rval_ins;
     nanojit::LIns*          inner_sp_ins;
     bool                    deepAborted;
+    bool                    applyingArguments;
     bool                    trashTree;
     nanojit::Fragment*      whichTreeToTrash;
     Queue<jsbytecode*>      cfgMerges;
     jsval*                  global_dslots;
     JSTraceableNative*      pendingTraceableNative;
     bool                    terminate;
     intptr_t                terminate_ip_adj;
     nanojit::Fragment*      outerToBlacklist;
@@ -381,19 +382,19 @@ class TraceRecorder : public avmplus::GC
     bool unbox_jsval(jsval v, nanojit::LIns*& v_ins);
     bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp);
     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, 
                               ExitType exitType);
     bool guardElemOp(JSObject* obj, nanojit::LIns* obj_ins, jsid id, size_t op_offset, jsval* vp);
     void clearFrameSlotsFromCache();
-    bool guardCallee(jsval& callee);
+    bool guardShapelessCallee(jsval& callee);
     bool interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc, bool constructing);
-    bool functionCall(bool constructing, uintN argc);
+    bool functionCall(bool constructing);
     bool forInLoop(jsval* vp);
 
     void trackCfgMerges(jsbytecode* pc);
     void flipIf(jsbytecode* pc, bool& cond);
     void fuseIf(jsbytecode* pc, bool cond, nanojit::LIns* x);
 
 public:
     friend bool js_MonitorRecording(TraceRecorder* tr);
@@ -420,17 +421,16 @@ public:
     
     bool record_EnterFrame();
     bool record_LeaveFrame();
     bool record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop);
     bool record_SetPropMiss(JSPropCacheEntry* entry);
     bool record_DefLocalFunSetSlot(uint32 slot, JSObject* obj);
     bool record_FastNativeCallComplete();
     bool record_IteratorNextComplete();
-    bool record_ApplyComplete(uintN argc);
     
     nanojit::Fragment* getOuterToBlacklist() { return outerToBlacklist; }
     void deepAbort() { deepAborted = true; }
     bool wasDeepAborted() { return deepAborted; }
     bool walkedOutOfLoop() { return terminate; }
     void setPromotedPeer(nanojit::Fragment* peer) { promotedPeer = peer; }
     TreeInfo* getTreeInfo() { return treeInfo; }
     
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -2303,25 +2303,16 @@ function testStringify() {
 	a[10] = "" + s;
 	a[11] = s + "";
     }
     return a.join(",");
 }
 testStringify.expected = "true,true,false,false,undefined,undefined,5,5,5.5,5.5,x,x";
 test(testStringify);
 
-function testApply() {
-    var q = [];
-    for (var i = 0; i < 10; ++i)
-        Array.prototype.push.apply(q, [5]);
-    return q.join(",");
-}
-testApply.expected = "5,5,5,5,5,5,5,5,5,5";
-test(testApply);
-
 /* NOTE: Keep this test last, since it screws up all for...in loops after it. */
 function testGlobalProtoAccess() {
     return "ok";
 }
 this.__proto__.a = 3; for (var j = 0; j < 4; ++j) { [a]; }
 testGlobalProtoAccess.expected = "ok";
 test(testGlobalProtoAccess);