[INFER] More fixes for stock JM behavior, bug 647048.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 01 Apr 2011 17:26:34 -0700
changeset 74884 baccdc943514c3cd483304476627009ae85f4196
parent 74883 0b1dd5e20bb95d0550d3002ab5f36599c21fba3a
child 74885 c340841f04651a570ba8ff7e957c729f34b069db
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs647048
milestone2.0b13pre
[INFER] More fixes for stock JM behavior, bug 647048.
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.h
js/src/jsdbgapi.cpp
js/src/jsinferinlines.h
js/src/jswrapper.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastBuiltins.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/FrameEntry.h
js/src/methodjit/FrameState-inl.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/StubCalls.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3065,19 +3065,19 @@ JS_NewCompartmentAndGlobalObject(JSConte
     CHECK_REQUEST(cx);
     JSCompartment *compartment = NewCompartment(cx, principals);
     if (!compartment)
         return NULL;
 
     AutoHoldCompartment hold(compartment);
 
     JSCompartment *saved = cx->compartment;
-    cx->compartment = compartment;
+    cx->setCompartment(compartment);
     JSObject *obj = JS_NewGlobalObject(cx, clasp);
-    cx->compartment = saved;
+    cx->setCompartment(saved);
 
     return obj;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -102,16 +102,18 @@
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsscope.h"
 #include "jsstr.h"
 #include "jsstaticcheck.h"
 #include "jstracer.h"
 #include "jsvector.h"
 #include "jswrapper.h"
+#include "methodjit/StubCalls.h"
+#include "methodjit/StubCalls-inl.h"
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jscntxtinlines.h"
 #include "jsinferinlines.h"
@@ -662,18 +664,21 @@ array_length_setter(JSContext *cx, JSObj
          * shrink slots here, shrink the initialized length too.  This permits us
          * us to disregard length when reading from arrays as long we are within
          * the initialized capacity.
          */
         jsuint oldcap = obj->getDenseArrayCapacity();
         if (oldcap > newlen)
             obj->shrinkDenseArrayElements(cx, newlen);
         jsuint oldinit = obj->getDenseArrayInitializedLength();
-        if (oldinit > newlen)
+        if (oldinit > newlen) {
             obj->setDenseArrayInitializedLength(newlen);
+            if (!cx->typeInferenceEnabled())
+                obj->backfillDenseArrayHoles();
+        }
     } else if (oldlen - newlen < (1 << 24)) {
         do {
             --oldlen;
             if (!JS_CHECK_OPERATION_LIMIT(cx)) {
                 JS_ALWAYS_TRUE(obj->setArrayLength(cx, oldlen + 1));
                 return false;
             }
             int deletion = DeleteArrayElement(cx, obj, oldlen, strict);
@@ -3494,20 +3499,21 @@ NewArray(JSContext *cx, jsuint length, J
         return NULL;
 
     if (!obj->setArrayLength(cx, length))
         return NULL;
 
     if (allocateCapacity) {
         if (!obj->ensureSlots(cx, length))
             return NULL;
-        if (!cx->typeInferenceEnabled())
-            obj->backfillDenseArrayHoles();
     }
 
+    if (!cx->typeInferenceEnabled())
+        obj->backfillDenseArrayHoles();
+
     return obj;
 }
 
 JSObject * JS_FASTCALL
 NewDenseEmptyArray(JSContext *cx, JSObject *proto)
 {
     return NewArray<false>(cx, 0, proto);
 }
@@ -3516,46 +3522,57 @@ JSObject * JS_FASTCALL
 NewDenseAllocatedArray(JSContext *cx, uint32 length, JSObject *proto)
 {
     return NewArray<true>(cx, length, proto);
 }
 
 JSObject * JS_FASTCALL
 NewDenseAllocatedEmptyArray(JSContext *cx, uint length, JSObject *proto)
 {
-    JSObject *obj = NewArray<true>(cx, length, proto);
-    if (!obj)
-        return NULL;
-    obj->setDenseArrayInitializedLength(length);
-    if (!obj->setDenseArrayNotPacked(cx))
-        return NULL;
-    ClearValueRange(obj->getSlots(), length, true);
-    return obj;
+    return NewArray<true>(cx, length, proto);
 }
 
 JSObject * JS_FASTCALL
 NewDenseUnallocatedArray(JSContext *cx, uint32 length, JSObject *proto)
 {
     return NewArray<false>(cx, length, proto);
 }
 
+#ifdef JS_METHODJIT
+JSObject * JS_FASTCALL
+mjit::stubs::NewDenseUnallocatedArray(VMFrame &f, uint32 length)
+{
+    JSObject *proto = (JSObject *) f.scratch;
+    JSObject *obj = NewArray<false>(f.cx, length, proto);
+    if (!obj) {
+        js_ReportOutOfMemory(f.cx);
+        THROWV(NULL);
+    }
+    return obj;
+}
+#endif
+
 JSObject *
 NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto)
 {
     JSObject* obj = NewArray<true>(cx, length, proto);
     if (!obj)
         return NULL;
 
     JS_ASSERT(obj->getDenseArrayCapacity() >= length);
 
-    if (vp) {
+    if (cx->typeInferenceEnabled()) {
+        if (vp) {
+            memcpy(obj->getDenseArrayElements(), vp, length * sizeof(Value));
+            obj->setDenseArrayInitializedLength(length);
+        } else {
+            obj->setDenseArrayInitializedLength(0);
+        }
+    } else if (vp) {
         memcpy(obj->getDenseArrayElements(), vp, length * sizeof(Value));
-        obj->setDenseArrayInitializedLength(length);
-    } else {
-        obj->setDenseArrayInitializedLength(0);
     }
 
     return obj;
 }
 
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_2(extern, OBJECT, NewDenseEmptyArray, CONTEXT, OBJECT, 0,
                      nanojit::ACCSET_STORE_ANY)
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -84,19 +84,26 @@ JSObject::setDenseArrayNotPacked(JSConte
     }
     return true;
 }
 
 inline JSObject::EnsureDenseResult
 JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra)
 {
     JS_ASSERT(isDenseArray());
+
     uintN currentCapacity = numSlots();
     uintN initLength = getDenseArrayInitializedLength();
 
+    /*
+     * Don't take excessive slow paths when inference is disabled, due to
+     * uninitialized slots between initializedLength and capacity.
+     */
+    JS_ASSERT_IF(!cx->typeInferenceEnabled(), currentCapacity == initLength);
+
     uintN requiredCapacity;
     if (extra == 1) {
         /* Optimize for the common case. */
         if (index < initLength)
             return ED_OK;
         if (index < currentCapacity) {
             if (index > initLength) {
                 if (!setDenseArrayNotPacked(cx))
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1953,16 +1953,17 @@ JSContext::resetCompartment()
          * can only fail due to bugs in the engine or embedding.)
          */
         OBJ_TO_INNER_OBJECT(this, scopeobj);
         if (!scopeobj)
             goto error;
     }
 
     compartment = scopeobj->compartment();
+    inferenceEnabled = compartment->types.inferenceEnabled;
 
     if (isExceptionPending())
         wrapPendingException();
     return;
 
 error:
 
     /*
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1643,16 +1643,18 @@ struct JSContext
     size_t              scriptStackQuota;
 
     /* Data shared by threads in an address space. */
     JSRuntime *const    runtime;
 
     /* GC heap compartment. */
     JSCompartment       *compartment;
 
+    inline void setCompartment(JSCompartment *compartment);
+
     /* Currently executing frame and regs, set by stack operations. */
     JS_REQUIRES_STACK
     JSFrameRegs         *regs;
 
     /* Current frame accessors. */
 
     JSStackFrame* fp() {
         JS_ASSERT(regs && regs->fp);
@@ -1934,16 +1936,18 @@ struct JSContext
     bool                 traceJitEnabled;
 #endif
 
 #ifdef JS_METHODJIT
     bool                 methodJitEnabled;
     bool                 profilingEnabled;
 #endif
 
+    bool                 inferenceEnabled;
+
     /* Caller must be holding runtime->gcLock. */
     void updateJITEnabled();
 
 #ifdef MOZ_TRACE_JSCALLS
     /* Function entry/exit debugging callback. */
     JSFunctionCallback    functionCallback;
 
     void doFunctionCallback(const JSFunction *fun,
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -567,16 +567,23 @@ TRACE_PROFILER(JSContext *cx)
 namespace js {
 static inline MathCache *
 GetMathCache(JSContext *cx)
 {
     return cx->compartment->getMathCache(cx);
 }
 }
 
+inline void
+JSContext::setCompartment(JSCompartment *compartment)
+{
+    this->compartment = compartment;
+    this->inferenceEnabled = compartment ? compartment->types.inferenceEnabled : false;
+}
+
 #ifdef DEBUG
 # define EVAL_CACHE_METER(x)    (cx->compartment->evalCacheMeter.x++)
 #else
 # define EVAL_CACHE_METER(x)    ((void) 0)
 #endif
 
 #ifdef _MSC_VER
 #pragma warning(pop)
@@ -592,35 +599,35 @@ class PreserveCompartment {
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
   public:
      PreserveCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) : cx(cx) {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         oldCompartment = cx->compartment;
     }
 
     ~PreserveCompartment() {
-        cx->compartment = oldCompartment;
+        cx->setCompartment(oldCompartment);
     }
 };
 
 class SwitchToCompartment : public PreserveCompartment {
   public:
     SwitchToCompartment(JSContext *cx, JSCompartment *newCompartment
                         JS_GUARD_OBJECT_NOTIFIER_PARAM)
         : PreserveCompartment(cx)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
-        cx->compartment = newCompartment;
+        cx->setCompartment(newCompartment);
     }
 
     SwitchToCompartment(JSContext *cx, JSObject *target JS_GUARD_OBJECT_NOTIFIER_PARAM)
         : PreserveCompartment(cx)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
-        cx->compartment = target->getCompartment();
+        cx->setCompartment(target->getCompartment());
     }
 
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AssertCompartmentUnchanged {
   protected:
     JSContext * const cx;
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -397,22 +397,22 @@ JS_ClearTrap(JSContext *cx, JSScript *sc
     if (trap)
         DestroyTrapAndUnlock(cx, trap);
     else
         DBG_UNLOCK(cx->runtime);
 
 #ifdef JS_METHODJIT
     if (script->hasJITCode()) {
         JSCompartment *oldCompartment = cx->compartment;
-        cx->compartment = script->compartment;
+        cx->setCompartment(script->compartment);
 
         mjit::Recompiler recompiler(cx, script);
         recompiler.recompile();
 
-        cx->compartment = oldCompartment;
+        cx->setCompartment(oldCompartment);
     }
 #endif
 }
 
 JS_PUBLIC_API(void)
 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
 {
     JSRuntime *rt;
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -221,17 +221,17 @@ UseNewType(JSContext *cx, JSScript *scri
 
 /////////////////////////////////////////////////////////////////////
 // JSContext
 /////////////////////////////////////////////////////////////////////
 
 inline bool
 JSContext::typeInferenceEnabled()
 {
-    return compartment->types.inferenceEnabled;
+    return inferenceEnabled;
 }
 
 inline js::types::TypeObject *
 JSContext::getTypeNewObject(JSProtoKey key)
 {
     JSObject *proto;
     if (!js_GetClassPrototype(this, NULL, key, &proto, NULL))
         return NULL;
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -384,23 +384,23 @@ AutoCompartment::~AutoCompartment()
 
 bool
 AutoCompartment::enter()
 {
     JS_ASSERT(!entered);
     if (origin != destination) {
         LeaveTrace(context);
 
-        context->compartment = destination;
+        context->setCompartment(destination);
         JSObject *scopeChain = target->getGlobal();
         JS_ASSERT(scopeChain->isNative());
 
         frame.construct();
         if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref())) {
-            context->compartment = origin;
+            context->setCompartment(origin);
             return false;
         }
 
         if (context->isExceptionPending())
             context->wrapPendingException();
     }
     entered = true;
     return true;
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1911,21 +1911,25 @@ mjit::Compiler::generateMethod()
             jsbytecode *next = &PC[JSOP_SETELEM_LENGTH];
             bool pop = (JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next));
             if (!jsop_setelem(pop))
                 return Compile_Error;
           }
           END_CASE(JSOP_SETELEM);
 
           BEGIN_CASE(JSOP_CALLNAME)
+          {
+            uint32 index = fullAtomIndex(PC);
             prepareStubCall(Uses(0));
-            masm.move(Imm32(fullAtomIndex(PC)), Registers::ArgReg1);
+            masm.move(Imm32(index), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::CallName);
             pushSyncedEntry(0);
             pushSyncedEntry(1);
+            frame.extra(frame.peek(-2)).name = script->getAtom(index);
+          }
           END_CASE(JSOP_CALLNAME)
 
           BEGIN_CASE(JSOP_EVAL)
           {
             JaegerSpew(JSpew_Insns, " --- EVAL --- \n");
             emitEval(GET_ARGC(PC));
             JaegerSpew(JSpew_Insns, " --- END EVAL --- \n");
           }
@@ -1954,17 +1958,21 @@ mjit::Compiler::generateMethod()
                 JaegerSpew(JSpew_Insns, " --- SCRIPTED CALL --- \n");
                 inlineCallHelper(GET_ARGC(PC), false);
                 JaegerSpew(JSpew_Insns, " --- END SCRIPTED CALL --- \n");
             }
           }
           END_CASE(JSOP_CALL)
 
           BEGIN_CASE(JSOP_NAME)
-            jsop_name(script->getAtom(fullAtomIndex(PC)), knownPushedType(0));
+          {
+            JSAtom *atom = script->getAtom(fullAtomIndex(PC));
+            jsop_name(atom, knownPushedType(0));
+            frame.extra(frame.peek(-1)).name = atom;
+          }
           END_CASE(JSOP_NAME)
 
           BEGIN_CASE(JSOP_DOUBLE)
           {
             uint32 index = fullAtomIndex(PC);
             double d = script->getConst(index).toDouble();
             frame.push(Value(DoubleValue(d)));
           }
@@ -2475,19 +2483,23 @@ mjit::Compiler::generateMethod()
           END_CASE(JSOP_DEFLOCALFUN)
 
           BEGIN_CASE(JSOP_RETRVAL)
             emitReturn(NULL);
           END_CASE(JSOP_RETRVAL)
 
           BEGIN_CASE(JSOP_GETGNAME)
           BEGIN_CASE(JSOP_CALLGNAME)
-            jsop_getgname(fullAtomIndex(PC), knownPushedType(0));
+          {
+            uint32 index = fullAtomIndex(PC);
+            jsop_getgname(index, knownPushedType(0));
+            frame.extra(frame.peek(-1)).name = script->getAtom(index);
             if (op == JSOP_CALLGNAME)
                 jsop_callgname_epilogue();
+          }
           END_CASE(JSOP_GETGNAME)
 
           BEGIN_CASE(JSOP_SETGNAME)
             jsop_setgname(script->getAtom(fullAtomIndex(PC)), true);
           END_CASE(JSOP_SETGNAME)
 
           BEGIN_CASE(JSOP_REGEXP)
           {
@@ -2624,18 +2636,21 @@ mjit::Compiler::generateMethod()
             /*
              * Inform the frame of the type sets for values just pushed. Skip
              * this if we did any opcode fusions, we don't keep track of the
              * associated type sets in such cases.
              */
             unsigned nuses = analyze::GetUseCount(script, oldPC - script->code);
             unsigned ndefs = analyze::GetDefCount(script, oldPC - script->code);
             for (unsigned i = 0; i < ndefs; i++) {
-                frame.learnTypeSet(opinfo->stackDepth - nuses + i,
-                                   script->types->pushed(oldPC - script->code, i));
+                FrameEntry *fe = frame.getStack(opinfo->stackDepth - nuses + i);
+                if (fe) {
+                    /* fe may be NULL for conditionally pushed entries, e.g. JSOP_AND */
+                    frame.extra(fe).types = script->types->pushed(oldPC - script->code, i);
+                }
             }
         }
 
 #ifdef DEBUG
         frame.assertValidRegisterState();
 #endif
     }
 
@@ -3241,16 +3256,22 @@ mjit::Compiler::inlineCallHelper(uint32 
 
     FrameEntry *origCallee = frame.peek(-(speculatedArgc + 2));
     FrameEntry *origThis = frame.peek(-(speculatedArgc + 1));
 
     /* 'this' does not need to be synced for constructing. */
     if (callingNew)
         frame.discardFe(origThis);
 
+    if (!cx->typeInferenceEnabled()) {
+        CompileStatus status = callArrayBuiltin(callImmArgc, callingNew);
+        if (status != Compile_InlineAbort)
+            return status;
+    }
+
     /*
      * From the presence of JSOP_FUN{CALL,APPLY}, we speculate that we are
      * going to call js_fun_{call,apply}. Normally, this call would go through
      * js::Invoke to ultimately call 'this'. We can do much better by having
      * the callIC cache and call 'this' directly. However, if it turns out that
      * we are not actually calling js_fun_call, the callIC must act as normal.
      *
      * Note: do *NOT* use type information or inline state in any way when
@@ -3373,20 +3394,20 @@ mjit::Compiler::inlineCallHelper(uint32 
     if (callIC.typeMonitored && callIC.frameSize.isStatic()) {
         unsigned argc = callIC.frameSize.staticArgc();
         callIC.argTypes = (types::ClonedTypeSet *)
             js_calloc((1 + argc) * sizeof(types::ClonedTypeSet));
         if (!callIC.argTypes) {
             js_ReportOutOfMemory(cx);
             return false;
         }
-        types::TypeSet *types = frame.getTypeSet(frame.peek(-(argc + 1)));
+        types::TypeSet *types = frame.extra(frame.peek(-(argc + 1))).types;
         types::TypeSet::Clone(cx, types, &callIC.argTypes[0]);
         for (unsigned i = 0; i < argc; i++) {
-            types::TypeSet *types = frame.getTypeSet(frame.peek(-(argc - i)));
+            types::TypeSet *types = frame.extra(frame.peek(-(argc - i))).types;
             types::TypeSet::Clone(cx, types, &callIC.argTypes[i + 1]);
         }
     }
 
     /* Test the type if necessary. Failing this always takes a really slow path. */
     MaybeJump notObjectJump;
     if (icCalleeType.isSet())
         notObjectJump = masm.testObject(Assembler::NotEqual, icCalleeType.reg());
@@ -3582,16 +3603,101 @@ mjit::Compiler::inlineCallHelper(uint32 
     }
 
     applyTricks = NoApplyTricks;
 
     return true;
 #endif
 }
 
+CompileStatus
+mjit::Compiler::callArrayBuiltin(uint32 argc, bool callingNew)
+{
+    if (applyTricks == LazyArgsObj)
+        return Compile_InlineAbort;
+
+    FrameEntry *origCallee = frame.peek(-(argc + 2));
+    if (origCallee->isNotType(JSVAL_TYPE_OBJECT))
+        return Compile_InlineAbort;
+
+    if (frame.extra(origCallee).name != cx->runtime->atomState.classAtoms[JSProto_Array])
+        return Compile_InlineAbort;
+
+    JSObject *arrayObj;
+    if (!js_GetClassObject(cx, globalObj, JSProto_Array, &arrayObj))
+        return Compile_Error;
+
+    JSObject *arrayProto;
+    if (!js_GetClassPrototype(cx, globalObj, JSProto_Array, &arrayProto))
+        return Compile_Error;
+
+    if (argc > 1)
+        return Compile_InlineAbort;
+    FrameEntry *origArg = (argc == 1) ? frame.peek(-1) : NULL;
+    if (origArg) {
+        if (origArg->isNotType(JSVAL_TYPE_INT32))
+            return Compile_InlineAbort;
+        if (origArg->isConstant() && origArg->getValue().toInt32() < 0)
+            return Compile_InlineAbort;
+    }
+
+    if (!origCallee->isTypeKnown()) {
+        Jump notObject = frame.testObject(Assembler::NotEqual, origCallee);
+        stubcc.linkExit(notObject, Uses(argc + 2));
+    }
+
+    RegisterID reg = frame.tempRegForData(origCallee);
+    Jump notArray = masm.branchPtr(Assembler::NotEqual, reg, ImmPtr(arrayObj));
+    stubcc.linkExit(notArray, Uses(argc + 2));
+
+    int32 knownSize = 0;
+    MaybeRegisterID sizeReg;
+    if (origArg) {
+        if (origArg->isConstant()) {
+            knownSize = origArg->getValue().toInt32();
+        } else {
+            if (!origArg->isTypeKnown()) {
+                Jump notInt = frame.testInt32(Assembler::NotEqual, origArg);
+                stubcc.linkExit(notInt, Uses(argc + 2));
+            }
+            sizeReg = frame.tempRegForData(origArg);
+            Jump belowZero = masm.branch32(Assembler::LessThan, sizeReg.reg(), Imm32(0));
+            stubcc.linkExit(belowZero, Uses(argc + 2));
+        }
+    } else {
+        knownSize = 0;
+    }
+
+    stubcc.leave();
+    stubcc.masm.move(Imm32(argc), Registers::ArgReg1);
+    OOL_STUBCALL(callingNew ? stubs::SlowNew : stubs::SlowCall);
+
+    {
+        PinRegAcrossSyncAndKill p1(frame, sizeReg);
+        frame.popn(argc + 2);
+        frame.syncAndKill(Uses(0));
+    }
+
+    prepareStubCall(Uses(0));
+    masm.storePtr(ImmPtr(arrayProto), FrameAddress(offsetof(VMFrame, scratch)));
+    if (sizeReg.isSet())
+        masm.move(sizeReg.reg(), Registers::ArgReg1);
+    else
+        masm.move(Imm32(knownSize), Registers::ArgReg1);
+    INLINE_STUBCALL(stubs::NewDenseUnallocatedArray);
+
+    frame.takeReg(Registers::ReturnReg);
+    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
+    frame.forgetType(frame.peek(-1));
+
+    stubcc.rejoin(Changes(1));
+
+    return Compile_Okay;
+}
+
 /* Maximum number of calls we will inline at the same site. */
 static const uint32 INLINE_SITE_LIMIT = 5;
 
 CompileStatus
 mjit::Compiler::inlineScriptedFunction(uint32 argc, bool callingNew)
 {
     JS_ASSERT(inlining);
 
@@ -3603,17 +3709,17 @@ mjit::Compiler::inlineScriptedFunction(u
         return Compile_InlineAbort;
 
     if (applyTricks == LazyArgsObj)
         return Compile_InlineAbort;
 
     FrameEntry *origCallee = frame.peek(-(argc + 2));
     FrameEntry *origThis = frame.peek(-(argc + 1));
 
-    types::TypeSet *types = frame.getTypeSet(origCallee);
+    types::TypeSet *types = frame.extra(origCallee).types;
     if (!types || types->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT)
         return Compile_InlineAbort;
 
     /*
      * Make sure no callees have had their .arguments accessed, and trigger
      * recompilation if they ever are accessed.
      */
     types::ObjectKind kind = types->getKnownObjectKind(cx);
@@ -4492,17 +4598,17 @@ mjit::Compiler::testSingletonProperty(JS
     return true;
 }
 
 bool
 mjit::Compiler::testSingletonPropertyTypes(FrameEntry *top, jsid id, bool *testObject)
 {
     *testObject = false;
 
-    types::TypeSet *types = frame.getTypeSet(top);
+    types::TypeSet *types = frame.extra(top).types;
     if (!types)
         return false;
 
     JSObject *singleton = types->getSingleton(cx);
     if (singleton)
         return testSingletonProperty(singleton, id);
 
     if (!script->compileAndGo)
@@ -4614,17 +4720,17 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
 
     ic::PICInfo::Kind kind = (op == JSOP_SETMETHOD)
                              ? ic::PICInfo::SETMETHOD
                              : ic::PICInfo::SET;
     PICGenInfo pic(kind, op, usePropCache);
     pic.atom = atom;
 
     if (monitored(PC)) {
-        types::TypeSet *types = frame.getTypeSet(rhs);
+        types::TypeSet *types = frame.extra(rhs).types;
         pic.typeMonitored = true;
         pic.rhsTypes = (types::ClonedTypeSet *) ::js_calloc(sizeof(types::ClonedTypeSet));
         if (!pic.rhsTypes) {
             js_ReportOutOfMemory(cx);
             return false;
         }
         types::TypeSet::Clone(cx, types, pic.rhsTypes);
     } else {
@@ -6043,17 +6149,20 @@ mjit::Compiler::jsop_newinit()
     if (isArray) {
         masm.move(Imm32(count), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::NewInitArray);
     } else {
         masm.move(ImmPtr(baseobj), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::NewInitObject);
     }
     frame.takeReg(Registers::ReturnReg);
-    frame.pushInitializerObject(Registers::ReturnReg, *PC == JSOP_NEWARRAY, baseobj);
+    frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
+
+    frame.extra(frame.peek(-1)).initArray = (*PC == JSOP_NEWARRAY);
+    frame.extra(frame.peek(-1)).initObject = baseobj;
 
     return true;
 }
 
 bool
 mjit::Compiler::finishLoop(jsbytecode *head)
 {
     if (!cx->typeInferenceEnabled())
@@ -6206,37 +6315,42 @@ mjit::Compiler::jumpAndTrace(Jump j, jsb
     Label traceStart = stubcc.masm.label();
 
     stubcc.linkExitDirect(j, traceStart);
     if (slow)
         slow->linkTo(traceStart, &stubcc.masm);
 
 # if JS_MONOIC
     ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
+
+    Jump nonzero = stubcc.masm.branchSub32(Assembler::NonZero, Imm32(1),
+                                           Address(Registers::ArgReg1,
+                                                   offsetof(TraceICInfo, loopCounter)));
 # endif
 
     /* Save and restore compiler-tracked PC, so cx->regs is right in InvokeTracer. */
     {
         jsbytecode* pc = PC;
         PC = target;
 
         OOL_STUBCALL(stubs::InvokeTracer);
 
         PC = pc;
     }
 
     Jump no = stubcc.masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg,
                                         Registers::ReturnReg);
+    if (!cx->typeInferenceEnabled())
+        stubcc.masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.fp)), JSFrameReg);
     stubcc.masm.jump(Registers::ReturnReg);
     no.linkTo(stubcc.masm.label(), &stubcc.masm);
 
-    if (!cx->typeInferenceEnabled())
-        stubcc.masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.fp)), JSFrameReg);
-
 #ifdef JS_MONOIC
+    nonzero.linkTo(stubcc.masm.label(), &stubcc.masm);
+
     ic.jumpTarget = target;
     ic.fastTrampoline = !consistent;
     ic.trampolineStart = stubcc.masm.label();
 
     traceICs[index] = ic;
 #endif
 
     /*
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -675,16 +675,17 @@ class Compiler : public BaseCompiler
           default:
             JS_NOT_REACHED("unrecognized op");
             return Assembler::Equal;
         }
     }
 
     /* Fast builtins. */
     JSObject *pushedSingleton(unsigned pushed);
+    CompileStatus callArrayBuiltin(uint32 argc, bool callingNew);
     CompileStatus inlineNativeFunction(uint32 argc, bool callingNew);
     CompileStatus inlineScriptedFunction(uint32 argc, bool callingNew);
     CompileStatus compileMathAbsInt(FrameEntry *arg);
     CompileStatus compileMathAbsDouble(FrameEntry *arg);
     CompileStatus compileMathSqrt(FrameEntry *arg);
     CompileStatus compileMathPowSimple(FrameEntry *arg1, FrameEntry *arg2);
 
     enum RoundingMode { Floor, Round };
--- a/js/src/methodjit/FastBuiltins.cpp
+++ b/js/src/methodjit/FastBuiltins.cpp
@@ -316,17 +316,17 @@ mjit::Compiler::compileGetChar(FrameEntr
 }
 
 
 CompileStatus
 mjit::Compiler::inlineNativeFunction(uint32 argc, bool callingNew)
 {
     JS_ASSERT(!callingNew);
 
-    if (cx->typeInferenceEnabled())
+    if (!cx->typeInferenceEnabled())
         return Compile_InlineAbort;
 
     if (applyTricks == LazyArgsObj)
         return Compile_InlineAbort;
 
     FrameEntry *origCallee = frame.peek(-(argc + 2));
     FrameEntry *thisValue = frame.peek(-(argc + 1));
 
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -478,18 +478,18 @@ mjit::Compiler::jsop_equality(JSOp op, B
 
     if (cx->typeInferenceEnabled() &&
         lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT)) {
         /*
          * Handle equality between two objects. We have to ensure there is no
          * special equality operator on either object, if that passes then
          * this is a pointer comparison.
          */
-        types::TypeSet *lhsTypes = frame.getTypeSet(lhs);
-        types::TypeSet *rhsTypes = frame.getTypeSet(rhs);
+        types::TypeSet *lhsTypes = frame.extra(lhs).types;
+        types::TypeSet *rhsTypes = frame.extra(rhs).types;
         types::ObjectKind lhsKind =
             lhsTypes ? lhsTypes->getKnownObjectKind(cx) : types::OBJECT_UNKNOWN;
         types::ObjectKind rhsKind =
             rhsTypes ? rhsTypes->getKnownObjectKind(cx) : types::OBJECT_UNKNOWN;
 
         if (lhsKind != types::OBJECT_UNKNOWN && rhsKind != types::OBJECT_UNKNOWN) {
             /* :TODO: Merge with jsop_relational_int? */
             JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
@@ -1184,17 +1184,17 @@ mjit::Compiler::jsop_setelem(bool popGua
     if (!IsCacheableSetElem(obj, id, value) || monitored(PC)) {
         jsop_setelem_slow();
         return true;
     }
 
     frame.forgetConstantData(obj);
 
     if (cx->typeInferenceEnabled()) {
-        types::TypeSet *types = frame.getTypeSet(obj);
+        types::TypeSet *types = frame.extra(obj).types;
         types::ObjectKind kind = types
             ? types->getKnownObjectKind(cx)
             : types::OBJECT_UNKNOWN;
         if (id->mightBeType(JSVAL_TYPE_INT32) &&
             (kind == types::OBJECT_DENSE_ARRAY || kind == types::OBJECT_PACKED_ARRAY) &&
             !arrayPrototypeHasIndexedProperty()) {
             // This is definitely a dense array, generate code directly without
             // using an inline cache.
@@ -1504,17 +1504,17 @@ mjit::Compiler::jsop_getelem(bool isCall
         else
             jsop_getelem_slow();
         return true;
     }
 
     frame.forgetConstantData(obj);
 
     if (cx->typeInferenceEnabled()) {
-        types::TypeSet *types = frame.getTypeSet(obj);
+        types::TypeSet *types = frame.extra(obj).types;
         types::ObjectKind kind = types
             ? types->getKnownObjectKind(cx)
             : types::OBJECT_UNKNOWN;
         if (!isCall && id->mightBeType(JSVAL_TYPE_INT32) &&
             (kind == types::OBJECT_DENSE_ARRAY || kind == types::OBJECT_PACKED_ARRAY) &&
             !arrayPrototypeHasIndexedProperty()) {
             // this is definitely a dense array, generate code directly without
             // using an inline cache.
@@ -1945,31 +1945,31 @@ void
 mjit::Compiler::jsop_initmethod()
 {
 #ifdef DEBUG
     FrameEntry *obj = frame.peek(-2);
 #endif
     JSAtom *atom = script->getAtom(fullAtomIndex(PC));
 
     /* Initializers with INITMETHOD are not fast yet. */
-    JS_ASSERT(!obj->initializerObject());
+    JS_ASSERT(!frame.extra(obj).initObject);
 
     prepareStubCall(Uses(2));
     masm.move(ImmPtr(atom), Registers::ArgReg1);
     INLINE_STUBCALL(stubs::InitMethod);
 }
 
 void
 mjit::Compiler::jsop_initprop()
 {
     FrameEntry *obj = frame.peek(-2);
     FrameEntry *fe = frame.peek(-1);
     JSAtom *atom = script->getAtom(fullAtomIndex(PC));
 
-    JSObject *baseobj = obj->initializerObject();
+    JSObject *baseobj = frame.extra(obj).initObject;
 
     if (!baseobj || monitored(PC)) {
         prepareStubCall(Uses(2));
         masm.move(ImmPtr(atom), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::InitProp);
         return;
     }
 
@@ -2000,17 +2000,17 @@ mjit::Compiler::jsop_initelem()
 
     /*
      * The initialized index is always a constant, but we won't remember which
      * constant if there are branches inside the code computing the initializer
      * expression (e.g. the expression uses the '?' operator).  Slow path those
      * cases, as well as those where INITELEM is used on an object initializer
      * or a non-fast array initializer.
      */
-    if (!id->isConstant() || !obj->initializerArray()) {
+    if (!id->isConstant() || !frame.extra(obj).initArray) {
         JSOp next = JSOp(PC[JSOP_INITELEM_LENGTH]);
 
         prepareStubCall(Uses(3));
         masm.move(Imm32(next == JSOP_ENDINIT ? 1 : 0), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::InitElem);
         return;
     }
 
--- a/js/src/methodjit/FrameEntry.h
+++ b/js/src/methodjit/FrameEntry.h
@@ -133,24 +133,16 @@ class FrameEntry
 
         Value newValue = Int32Value(value);
         setConstant(Jsvalify(newValue));
     }
 
     bool isCopy() const { return !!copy; }
     bool isCopied() const { return copied; }
 
-    inline bool initializerArray() {
-        return initArray;
-    }
-
-    inline JSObject *initializerObject() {
-        return initObject;
-    }
-
   private:
     void setType(JSValueType type_) {
         type.setConstant();
 #if defined JS_NUNBOX32
         v_.s.tag = JSVAL_TYPE_TO_TAG(type_);
 #elif defined JS_PUNBOX64
         v_.asBits &= JSVAL_PAYLOAD_MASK;
         v_.asBits |= JSVAL_TYPE_TO_SHIFTED_TAG(type_);
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -199,17 +199,17 @@ FrameState::pop()
     JS_ASSERT(sp > spBase);
 
     FrameEntry *fe = --sp;
     if (!fe->isTracked())
         return;
 
     forgetAllRegs(fe);
 
-    a->typeSets[fe - spBase] = NULL;
+    a->extraArray[fe - spBase].reset();
 }
 
 inline void
 FrameState::freeReg(AnyRegisterID reg)
 {
     JS_ASSERT(!regstate(reg).usedBy());
 
     a->freeRegs.putReg(reg);
@@ -233,17 +233,17 @@ FrameState::forgetReg(AnyRegisterID reg)
 inline FrameEntry *
 FrameState::rawPush()
 {
     JS_ASSERT(unsigned(sp - entries) < feLimit(script));
 
     if (!sp->isTracked())
         addToTracker(sp);
 
-    a->typeSets[sp - spBase] = NULL;
+    a->extraArray[sp - spBase].reset();
 
     return sp++;
 }
 
 inline void
 FrameState::push(const Value &v)
 {
     FrameEntry *fe = rawPush();
@@ -399,26 +399,16 @@ FrameState::pushInt32(RegisterID payload
     fe->type.setMemory();
 
     fe->data.unsync();
     fe->data.setRegister(payload);
     regstate(payload).associate(fe, RematInfo::DATA);
 }
 
 inline void
-FrameState::pushInitializerObject(RegisterID payload, bool array, JSObject *baseobj)
-{
-    pushTypedPayload(JSVAL_TYPE_OBJECT, payload);
-
-    FrameEntry *fe = peek(-1);
-    fe->initArray = array;
-    fe->initObject = baseobj;
-}
-
-inline void
 FrameState::pushUntypedPayload(JSValueType type, RegisterID payload)
 {
     JS_ASSERT(!a->freeRegs.hasReg(payload));
 
     FrameEntry *fe = rawPush();
 
     fe->clear();
 
@@ -865,30 +855,16 @@ FrameState::forgetType(FrameEntry *fe)
         fe->type.invalidate();
         return;
     }
 
     ensureTypeSynced(fe, masm);
     fe->type.setMemory();
 }
 
-inline types::TypeSet *
-FrameState::getTypeSet(FrameEntry *fe)
-{
-    JS_ASSERT(fe >= spBase && fe < sp);
-    return a->typeSets[fe - spBase];
-}
-
-inline void
-FrameState::learnTypeSet(unsigned slot, types::TypeSet *types)
-{
-    if (slot < unsigned(sp - spBase))
-        a->typeSets[slot] = types;
-}
-
 inline void
 FrameState::learnType(FrameEntry *fe, JSValueType type, bool unsync)
 {
     JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
     if (fe->type.inRegister())
         forgetReg(fe->type.reg());
     fe->setType(type);
     if (unsync)
@@ -1053,17 +1029,18 @@ FrameState::getOrTrack(uint32 index)
         fe->resetSynced();
     }
     return fe;
 }
 
 inline FrameEntry *
 FrameState::getStack(uint32 slot)
 {
-    JS_ASSERT(slot < uint32(sp - spBase));
+    if (slot >= uint32(sp - spBase))
+        return NULL;
     return getOrTrack(uint32(&spBase[slot] - entries));
 }
 
 inline FrameEntry *
 FrameState::getLocal(uint32 slot)
 {
     JS_ASSERT(slot < script->nslots);
     return getOrTrack(uint32(&locals[slot] - entries));
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -122,17 +122,17 @@ FrameState::pushActiveFrame(JSScript *sc
     uint32 depth = a ? totalDepth() : 0;
 
     // nslots + nargs + 2 (callee, this)
     uint32 nentries = feLimit(script);
 
     size_t totalBytes = sizeof(ActiveFrame) +
                         sizeof(FrameEntry) * nentries +              // entries[]
                         sizeof(FrameEntry *) * nentries +            // tracker.entries
-                        sizeof(types::TypeSet *) * script->nslots;   // typeSets
+                        sizeof(StackEntryExtra) * script->nslots;    // extraArray
 
     uint8 *cursor = (uint8 *)cx->calloc(totalBytes);
     if (!cursor)
         return false;
 
     ActiveFrame *newa = (ActiveFrame *) cursor;
     cursor += sizeof(ActiveFrame);
 
@@ -159,18 +159,18 @@ FrameState::pushActiveFrame(JSScript *sc
     newa->callee_ = newa->entries;
     newa->this_ = newa->entries + 1;
     newa->args = newa->entries + 2;
     newa->locals = newa->args + (script->fun ? script->fun->nargs : 0);
 
     newa->tracker.entries = (FrameEntry **)cursor;
     cursor += sizeof(FrameEntry *) * nentries;
 
-    newa->typeSets = (types::TypeSet **)cursor;
-    cursor += sizeof(types::TypeSet *) * script->nslots;
+    newa->extraArray = (StackEntryExtra *)cursor;
+    cursor += sizeof(StackEntryExtra) * script->nslots;
 
     JS_ASSERT(reinterpret_cast<uint8 *>(newa) + totalBytes == cursor);
 
     this->a = newa;
     updateActiveFrame();
 
     if (a->parent && a->analysis->inlineable(argc)) {
         a->depth = depth + VALUES_PER_STACK_FRAME;
@@ -1103,17 +1103,18 @@ FrameState::discardForJoin(jsbytecode *t
 
         regstate(reg).associate(fe, RematInfo::DATA);
         if (!alloc->synced(reg))
             fe->data.unsync();
     }
 
     sp = spBase + stackDepth;
 
-    PodZero(a->typeSets, stackDepth);
+    for (unsigned i = 0; i < stackDepth; i++)
+        a->extraArray[i].reset();
 
     return true;
 }
 
 bool
 FrameState::consistentRegisters(jsbytecode *target)
 {
     if (!cx->typeInferenceEnabled()) {
@@ -2438,17 +2439,17 @@ FrameState::forgetEntry(FrameEntry *fe)
         uncopy(fe);
         if (!fe->isCopied())
             forgetAllRegs(fe);
     } else {
         forgetAllRegs(fe);
     }
 
     if (fe >= sp)
-        a->typeSets[fe - spBase] = NULL;
+        a->extraArray[fe - spBase].reset();
 }
 
 void
 FrameState::storeTop(FrameEntry *target, JSValueType type, bool popGuaranteed)
 {
     /* Detect something like (x = x) which is a no-op. */
     FrameEntry *top = peek(-1);
     if (top->isCopy() && top->copyOf() == target) {
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -322,22 +322,16 @@ class FrameState
      * Pushes an int32 onto the operation stack. This is a specialized version
      * of pushNumber. The caller must guarantee that (a) an int32 is to be 
      * pushed on the inline path, and (b) if any slow path pushes a double,
      * the slow path also stores the double to memory.
      */
     inline void pushInt32(RegisterID payload);
 
     /*
-     * Pushes an initializer with specified payload, storing whether it is an array
-     * or object whose contents can be initialized in fast paths.
-     */
-    inline void pushInitializerObject(RegisterID payload, bool array, JSObject *baseobj);
-
-    /*
      * Pops a value off the operation stack, freeing any of its resources.
      */
     inline void pop();
 
     /*
      * Pops a number of values off the operation stack, freeing any of their
      * resources.
      */
@@ -668,21 +662,28 @@ class FrameState
      */
     inline void forgetType(FrameEntry *fe);
 
     /*
      * Discards a FrameEntry, tricking the FS into thinking it's synced.
      */
     void discardFe(FrameEntry *fe);
 
-    /* Get a set with the possible types of a stack fe, or NULL. */
-    inline types::TypeSet *getTypeSet(FrameEntry *fe);
-
-    /* Mark a stack index as holding a particular type set. */
-    inline void learnTypeSet(unsigned slot, types::TypeSet *types);
+    /* Compiler-owned metadata about stack entries, reset on push/pop/copy. */
+    struct StackEntryExtra {
+        bool initArray;
+        JSObject *initObject;
+        types::TypeSet *types;
+        JSAtom *name;
+        void reset() { PodZero(this); }
+    };
+    StackEntryExtra& extra(FrameEntry *fe) {
+        JS_ASSERT(fe >= spBase && fe < sp);
+        return a->extraArray[fe - spBase];
+    }
 
     /*
      * Helper function. Tests if a slot's type is null. Condition must
      * be Equal or NotEqual.
      */
     inline Jump testNull(Assembler::Condition cond, FrameEntry *fe);
 
     /*
@@ -1026,18 +1027,18 @@ class FrameState
         FrameEntry *callee_;
         FrameEntry *this_;
         FrameEntry *args;
         FrameEntry *locals;
 
         /* Vector of tracked slot indexes. */
         Tracker tracker;
 
-        /* Type sets for the stack contents. */
-        types::TypeSet **typeSets;
+        /* Compiler-owned metadata for the stack contents. */
+        StackEntryExtra *extraArray;
 
         /*
          * Register ownership state. This can't be used alone; to find whether an
          * entry is active, you must check the allocated registers.
          */
         RegisterState regstate_[Registers::TotalAnyRegisters];
 
         const RegisterState & regstate(AnyRegisterID reg) const {
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -186,25 +186,29 @@ InlineReturn(VMFrame &f)
     Value *newsp = fp->actualArgs() - 1;
     newsp[-1] = fp->returnValue();
     cx->stack().popInlineFrame(cx, fp->prev(), newsp);
 }
 
 void JS_FASTCALL
 stubs::SlowCall(VMFrame &f, uint32 argc)
 {
+    printf("CALL\n");
+
     Value *vp = f.regs.sp - (argc + 2);
 
     if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
         THROW();
 }
 
 void JS_FASTCALL
 stubs::SlowNew(VMFrame &f, uint32 argc)
 {
+    printf("NEW\n");
+
     JSContext *cx = f.cx;
     Value *vp = f.regs.sp - (argc + 2);
 
     if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
         THROW();
 }
 
 /*
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -227,16 +227,19 @@ void JS_FASTCALL CheckArgumentTypes(VMFr
 void JS_FASTCALL AssertArgumentTypes(VMFrame &f);
 #endif
 
 template <bool strict> int32 JS_FASTCALL ConvertToTypedInt(JSContext *cx, Value *vp);
 void JS_FASTCALL ConvertToTypedFloat(JSContext *cx, Value *vp);
 
 void JS_FASTCALL Exception(VMFrame &f);
 
+JSObject * JS_FASTCALL
+NewDenseUnallocatedArray(VMFrame &f, uint32 length);
+
 } /* namespace stubs */
 
 /* 
  * If COND is true, return A; otherwise, return B. This allows us to choose between
  * function template instantiations without running afoul of C++'s overload resolution
  * rules. (Try simplifying, and you'll either see the problem --- or have found a
  * better solution!)
  */