[INFER] Optimize arguments accesses, bug 658638.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 26 May 2011 12:28:19 -0700
changeset 75112 81997070017e97efc8a005dbe03c946fb6c26f8f
parent 75111 8bcb569c9bf9b7ec8dbc4b0d62e2a2c433716837
child 75113 6ad7f6da94cb0531ec121193c07ffd8f264f516e
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs658638
milestone6.0a1
[INFER] Optimize arguments accesses, bug 658638.
js/src/jit-test/tests/jaeger/argumentsOptimize-1.js
js/src/jit-test/tests/jaeger/argumentsOptimize-2.js
js/src/jit-test/tests/jaeger/recompile/bug659766.js
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsdbgapi.cpp
js/src/jsemit.cpp
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsscript.h
js/src/jsval.h
js/src/jsvalue.h
js/src/methodjit/BaseAssembler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
js/src/methodjit/LoopState.cpp
js/src/methodjit/LoopState.h
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/Retcon.cpp
js/src/methodjit/StubCalls.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/argumentsOptimize-1.js
@@ -0,0 +1,14 @@
+
+function bar() {
+  foo.arguments.length = 10;
+}
+
+function foo(x) {
+  var a = arguments;
+  var n = 0;
+  bar();
+  assertEq(x, 5);
+  assertEq(a.length, 10);
+}
+
+foo(5);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/argumentsOptimize-2.js
@@ -0,0 +1,27 @@
+
+function foo() {
+  var x = 0;
+  for (var i = arguments.length - 1; i >= 0; i--)
+    x += arguments[i];
+  return x;
+}
+
+function bar() {
+  var x = 0;
+  for (var i = 0; i < arguments.length; i++)
+    x += arguments[i];
+  return x;
+}
+
+function baz(a,b,c,d,e) {
+  var x = 0;
+  for (var i = 0; i < arguments.length; i++)
+    x += arguments[i];
+  return x;
+}
+
+for (var i = 0; i < 10; i++) {
+  assertEq(foo(1,2,3,4,5), 15);
+  assertEq(bar(1,2.5,true,{valueOf:function() { return 10}},"five"), "14.5five");
+  assertEq(baz(1,2,3,4,5), 15);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/recompile/bug659766.js
@@ -0,0 +1,29 @@
+var gTestcases = new Array;
+var gTc = gTestcases;
+function TestCase(n, d, e, a) {
+  this.description=d
+  this.reason=''
+  gTestcases[gTc++]=this
+}
+TestCase.prototype.dump=function () + toPrinted(this.description) + toPrinted(this.reason) + '\n';
+function toPrinted(value) value=value.replace(/\\n/g, 'NL').replace(/[^\x20-\x7E]+/g, escapeString);
+function escapeString (str) {
+  try {
+     err
+  } catch(ex) { }
+}
+function jsTestDriverEnd() {
+  for (var i = 0; i < gTestcases.length; i++)
+  gTestcases[i].dump()
+}
+var SECTION = "dowhile-007";
+DoWhile();
+function DoWhile( object ) result1=false;
+new TestCase(
+    SECTION,
+    "break one: ",
+    result1 
+);
+jsTestDriverEnd();
+new TestCase( SECTION, "'�O� �:i��'.match(new RegExp('.+'))", [], '�O� �:i��');
+jsTestDriverEnd();
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -324,16 +324,20 @@ ScriptAnalysis::analyzeBytecode(JSContex
 
     isInlineable = true;
     if (script->nClosedArgs || script->nClosedVars || script->nfixed >= LOCAL_LIMIT ||
         (script->fun && script->fun->isHeavyweight()) ||
         script->usesEval || script->usesArguments || cx->compartment->debugMode) {
         isInlineable = false;
     }
 
+    modifiesArguments_ = false;
+    if (script->nClosedArgs || (script->fun && script->fun->isHeavyweight()))
+        modifiesArguments_ = true;
+
     canTrackVars = true;
 
     /*
      * If we are in the middle of one or more jumps, the offset of the highest
      * target jumping over this bytecode.  Includes implicit jumps from
      * try/catch/finally blocks.
      */
     unsigned forwardJump = 0;
@@ -636,25 +640,29 @@ ScriptAnalysis::analyzeBytecode(JSContex
                 } else {
                     /* This local is unconditionally defined by this bytecode. */
                     setLocal(local, offset);
                 }
             }
             break;
           }
 
-          /* Additional opcodes which can be compiled but which can't be inlined. */
-          case JSOP_ARGUMENTS:
-          case JSOP_EVAL:
           case JSOP_FORARG:
           case JSOP_SETARG:
           case JSOP_INCARG:
           case JSOP_DECARG:
           case JSOP_ARGINC:
           case JSOP_ARGDEC:
+            modifiesArguments_ = true;
+            isInlineable = false;
+            break;
+
+          /* Additional opcodes which can be compiled but which can't be inlined. */
+          case JSOP_ARGUMENTS:
+          case JSOP_EVAL:
           case JSOP_THROW:
           case JSOP_EXCEPTION:
           case JSOP_DEFLOCALFUN:
           case JSOP_DEFLOCALFUN_FC:
           case JSOP_LAMBDA:
           case JSOP_LAMBDA_FC:
           case JSOP_GETFCSLOT:
           case JSOP_CALLFCSLOT:
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -737,16 +737,22 @@ class SSAValue
     void initWritten(uint32 slot, uint32 offset) {
         clear();
         u.var.kind = VAR;
         u.var.initial = false;
         u.var.slot = slot;
         u.var.offset = offset;
     }
 
+    static SSAValue WrittenVar(uint32 slot, uint32 offset) {
+        SSAValue v;
+        v.initWritten(slot, offset);
+        return v;
+    }
+
     void initPhi(uint32 offset, SSAPhiNode *node) {
         clear();
         u.phi.kind = PHI;
         u.phi.offset = offset;
         u.phi.node = node;
     }
 
   private:
@@ -859,16 +865,17 @@ class ScriptAnalysis
 
     bool usesRval;
     bool usesScope;
     bool usesThis;
     bool hasCalls;
     bool canTrackVars;
     bool isInlineable;
     uint32 numReturnSites_;
+    bool modifiesArguments_;
 
     /* Offsets at which each local becomes unconditionally defined, or a value below. */
     uint32 *definedLocals;
 
     static const uint32 LOCAL_USE_BEFORE_DEF = uint32(-1);
     static const uint32 LOCAL_CONDITIONALLY_DEFINED = uint32(-2);
 
     /* --------- Lifetime analysis --------- */
@@ -901,16 +908,22 @@ class ScriptAnalysis
 
     /* Whether there are NAME bytecodes which can access the frame's scope chain. */
     bool usesScopeChain() const { return usesScope; }
 
     bool usesThisValue() const { return usesThis; }
     bool hasFunctionCalls() const { return hasCalls; }
     uint32 numReturnSites() const { return numReturnSites_; }
 
+    /*
+     * True if all named formal arguments are not modified. If the arguments
+     * object cannot escape, the arguments are never modified within the script.
+     */
+    bool modifiesArguments() { return modifiesArguments_; }
+
     /* Accessors for bytecode information. */
 
     Bytecode& getCode(uint32 offset) {
         JS_ASSERT(script->compartment->activeAnalysis);
         JS_ASSERT(offset < script->length);
         JS_ASSERT(codeArray[offset]);
         return *codeArray[offset];
     }
@@ -1146,16 +1159,18 @@ class ScriptAnalysis
         bool hasHole;
         TypeInferenceState(JSContext *cx)
             : phiNodes(cx), hasGetSet(false), hasHole(false)
         {}
     };
 
     /* Type inference helpers */
     bool analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferenceState &state);
+    bool followEscapingArguments(JSContext *cx, const SSAValue &v, Vector<SSAValue> *seen);
+    bool followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector<SSAValue> *seen);
     inline void setForTypes(JSContext *cx, jsbytecode *pc, types::TypeSet *types);
 };
 
 /* Protect analysis structures from GC while they are being used. */
 struct AutoEnterAnalysis
 {
     JSContext *cx;
     bool oldActiveAnalysis;
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -218,16 +218,20 @@ JS_SetDebugModeForCompartment(JSContext 
         if (!ac.entered() && !ac.enter(cx, script)) {
             comp->debugMode = JS_FALSE;
             return JS_FALSE;
         }
 
         mjit::ReleaseScriptCode(cx, script, true);
         mjit::ReleaseScriptCode(cx, script, false);
         script->debugMode = !!debug;
+
+        /* Mark arguments objects as escaping in all scripts if debug mode is on. */
+        if (script->usesArguments && debug)
+            cx->markTypeObjectFlags(script->fun->getType(), types::OBJECT_FLAG_CREATED_ARGUMENTS);
     }
 #endif
 
     return JS_TRUE;
 }
 
 JS_FRIEND_API(JSBool)
 js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -2819,17 +2819,18 @@ EmitPropOp(JSContext *cx, JSParseNode *p
         JS_ASSERT(pn->pn_type == TOK_DOT);
         JS_ASSERT(op == JSOP_GETPROP);
         op = JSOP_CALLPROP;
     } else if (op == JSOP_GETPROP && pn->pn_type == TOK_DOT) {
         if (pn2->pn_type == TOK_NAME) {
             /* Try to optimize arguments.length into JSOP_ARGCNT */
             if (!BindNameToSlot(cx, cg, pn2))
                 return JS_FALSE;
-            if (pn->pn_atom == cx->runtime->atomState.lengthAtom) {
+            if (!cx->typeInferenceEnabled() &&
+                pn->pn_atom == cx->runtime->atomState.lengthAtom) {
                 if (pn2->pn_op == JSOP_ARGUMENTS)
                     return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0;
             }
         }
     }
 
     /*
      * If the object operand is also a dotted property reference, reverse the
@@ -2911,16 +2912,17 @@ EmitElemOp(JSContext *cx, JSParseNode *p
          * one or more index expression and JSOP_GETELEM op pairs.
          */
         if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) {
             if (!BindNameToSlot(cx, cg, left))
                 return JS_FALSE;
             if (left->pn_op == JSOP_ARGUMENTS &&
                 JSDOUBLE_IS_INT32(next->pn_dval, &slot) &&
                 jsuint(slot) < JS_BIT(16) &&
+                !cx->typeInferenceEnabled() &&
                 (!cg->inStrictMode() ||
                  (!cg->mutatesParameter() && !cg->callsEval()))) {
                 /*
                  * arguments[i]() requires arguments object as "this".
                  * Check that we never generates list for that usage.
                  */
                 JS_ASSERT(op != JSOP_CALLELEM || next->pn_next);
                 left->pn_offset = next->pn_offset = top;
@@ -2987,16 +2989,17 @@ EmitElemOp(JSContext *cx, JSParseNode *p
         if (op == JSOP_GETELEM &&
             left->pn_type == TOK_NAME &&
             right->pn_type == TOK_NUMBER) {
             if (!BindNameToSlot(cx, cg, left))
                 return JS_FALSE;
             if (left->pn_op == JSOP_ARGUMENTS &&
                 JSDOUBLE_IS_INT32(right->pn_dval, &slot) &&
                 jsuint(slot) < JS_BIT(16) &&
+                !cx->typeInferenceEnabled() &&
                 (!cg->inStrictMode() ||
                  (!cg->mutatesParameter() && !cg->callsEval()))) {
                 left->pn_offset = right->pn_offset = top;
                 EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot);
                 return JS_TRUE;
             }
         }
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -104,16 +104,19 @@ JSObject::getThrowTypeError() const
     return getGlobal()->getThrowTypeError();
 }
 
 JSBool
 js_GetArgsValue(JSContext *cx, StackFrame *fp, Value *vp)
 {
     JSObject *argsobj;
 
+    cx->markTypeObjectFlags(fp->fun()->getType(),
+                            OBJECT_FLAG_CREATED_ARGUMENTS | OBJECT_FLAG_UNINLINEABLE);
+
     if (fp->hasOverriddenArgs()) {
         JS_ASSERT(fp->hasCallObj());
         jsid id = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
         return fp->callObj().getProperty(cx, id, vp);
     }
     argsobj = js_GetArgsObject(cx, fp);
     if (!argsobj)
         return JS_FALSE;
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -170,16 +170,18 @@ types::TypeString(jstype type)
       case TYPE_BOOLEAN:
         return "bool";
       case TYPE_INT32:
         return "int";
       case TYPE_DOUBLE:
         return "float";
       case TYPE_STRING:
         return "string";
+      case TYPE_LAZYARGS:
+        return "lazyargs";
       case TYPE_UNKNOWN:
         return "unknown";
       default: {
         JS_ASSERT(TypeIsObject(type));
         TypeObject *object = (TypeObject *) type;
         return object->name();
       }
     }
@@ -195,18 +197,18 @@ types::InferSpew(SpewChannel channel, co
     va_start(ap, fmt);
     fprintf(stdout, "[infer] ");
     vfprintf(stdout, fmt, ap);
     fprintf(stdout, "\n");
     va_end(ap);
 }
 
 /* Whether types can be considered to contain type or an equivalent, for checking results. */
-static inline bool
-TypeSetMatches(JSContext *cx, TypeSet *types, jstype type)
+bool
+types::TypeMatches(JSContext *cx, TypeSet *types, jstype type)
 {
     if (types->hasType(type))
         return true;
 
     /*
      * If this is a type for an object with unknown properties, match any object
      * in the type set which also has unknown properties. This avoids failure
      * on objects whose prototype (and thus type) changes dynamically, which will
@@ -248,17 +250,17 @@ types::TypeHasProperty(JSContext *cx, Ty
         if (cx->compartment->types.pendingCount)
             return true;
 
         jstype type = GetValueType(cx, value);
 
         AutoEnterTypeInference enter(cx);
 
         TypeSet *types = obj->getProperty(cx, id, false);
-        if (types && !TypeSetMatches(cx, types, type)) {
+        if (types && !TypeMatches(cx, types, type)) {
             TypeFailure(cx, "Missing type in object %s %s: %s",
                         obj->name(), TypeIdString(id), TypeString(type));
         }
     }
     return true;
 }
 
 #endif
@@ -286,17 +288,17 @@ types::TypeFailure(JSContext *cx, const 
 void
 TypeSet::addTypeSet(JSContext *cx, ClonedTypeSet *types)
 {
     if (types->typeFlags & TYPE_FLAG_UNKNOWN) {
         addType(cx, TYPE_UNKNOWN);
         return;
     }
 
-    for (jstype type = TYPE_UNDEFINED; type <= TYPE_STRING; type++) {
+    for (jstype type = TYPE_UNDEFINED; type < TYPE_UNKNOWN; type++) {
         if (types->typeFlags & (1 << type))
             addType(cx, type);
     }
 
     if (types->objectCount >= 2) {
         for (unsigned i = 0; i < types->objectCount; i++)
             addType(cx, (jstype) types->objectSet[i]);
     } else if (types->objectCount == 1) {
@@ -329,17 +331,17 @@ TypeSet::add(JSContext *cx, TypeConstrai
         return;
 
     if (typeFlags & TYPE_FLAG_UNKNOWN) {
         cx->compartment->types.addPending(cx, constraint, this, TYPE_UNKNOWN);
         cx->compartment->types.resolvePending(cx);
         return;
     }
 
-    for (jstype type = TYPE_UNDEFINED; type <= TYPE_STRING; type++) {
+    for (jstype type = TYPE_UNDEFINED; type < TYPE_UNKNOWN; type++) {
         if (typeFlags & (1 << type))
             cx->compartment->types.addPending(cx, constraint, this, type);
     }
 
     unsigned count = getObjectCount();
     for (unsigned i = 0; i < count; i++) {
         TypeObject *object = getObject(i);
         if (object)
@@ -375,16 +377,18 @@ TypeSet::print(JSContext *cx)
     if (typeFlags & TYPE_FLAG_BOOLEAN)
         printf(" bool");
     if (typeFlags & TYPE_FLAG_INT32)
         printf(" int");
     if (typeFlags & TYPE_FLAG_DOUBLE)
         printf(" float");
     if (typeFlags & TYPE_FLAG_STRING)
         printf(" string");
+    if (typeFlags & TYPE_FLAG_LAZYARGS)
+        printf(" lazyargs");
 
     if (objectCount) {
         printf(" object[%u]", objectCount);
 
         unsigned count = getObjectCount();
         for (unsigned i = 0; i < count; i++) {
             TypeObject *object = getObject(i);
             if (object)
@@ -779,16 +783,45 @@ public:
 
 void
 TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
 {
     add(cx, ArenaNew<TypeConstraintSubsetBarrier>(cx->compartment->pool, script, pc, target));
 }
 
 /*
+ * Constraint which marks a pushed ARGUMENTS value as unknown if the script has
+ * an arguments object created in the future.
+ */
+class TypeConstraintLazyArguments : public TypeConstraint
+{
+public:
+    jsbytecode *pc;
+    TypeSet *target;
+
+    TypeConstraintLazyArguments(JSScript *script, TypeSet *target)
+        : TypeConstraint("lazyArgs", script), target(target)
+    {}
+
+    void newType(JSContext *cx, TypeSet *source, jstype type) {}
+
+    void newObjectState(JSContext *cx, TypeObject *object, bool force)
+    {
+        if (object->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS))
+            target->addType(cx, TYPE_UNKNOWN);
+    }
+};
+
+void
+TypeSet::addLazyArguments(JSContext *cx, JSScript *script, TypeSet *target)
+{
+    add(cx, ArenaNew<TypeConstraintLazyArguments>(cx->compartment->pool, script, target));
+}
+
+/*
  * Type constraint which marks the result of 'for in' loops as unknown if the
  * iterated value could be a generator.
  */
 class TypeConstraintGenerator : public TypeConstraint
 {
 public:
     TypeSet *target;
 
@@ -855,16 +888,25 @@ GetPropertyObject(JSContext *cx, JSScrip
         return NULL;
     }
 
     if (!object)
         cx->compartment->types.setPendingNukeTypes(cx);
     return object;
 }
 
+static inline void
+MarkPropertyAccessUnknown(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
+{
+    if (CanHaveReadBarrier(pc))
+        script->analysis(cx)->addTypeBarrier(cx, pc, target, TYPE_UNKNOWN);
+    else
+        target->addType(cx, TYPE_UNKNOWN);
+}
+
 /*
  * Handle a property access on a specific object. All property accesses go through
  * here, whether via x.f, x[f], or global name accesses.
  */
 static inline void
 PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *object,
                bool assign, TypeSet *target, jsid id)
 {
@@ -883,17 +925,17 @@ PropertyAccess(JSContext *cx, JSScript *
         else
             target->addType(cx, TYPE_UNKNOWN);
         return;
     }
 
     /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */
     if (object->unknownProperties()) {
         if (!assign)
-            target->addType(cx, TYPE_UNKNOWN);
+            MarkPropertyAccessUnknown(cx, script, pc, target);
         return;
     }
 
     /* Capture the effects of a standard property access. */
     if (target) {
         TypeSet *types = object->getProperty(cx, id, assign);
         if (!types)
             return;
@@ -910,33 +952,46 @@ PropertyAccess(JSContext *cx, JSScript *
             return;
         readTypes->addArith(cx, script, writeTypes);
     }
 }
 
 void
 TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
 {
+    UntrapOpcode untrap(cx, script, pc);
+
     if (type == TYPE_UNKNOWN || (!TypeIsObject(type) && !script->global)) {
         /*
          * Access on an unknown object. Reads produce an unknown result, writes
          * need to be monitored. Note: this isn't a problem for handling overflows
          * on inc/dec below, as these go through a slow path which must call
          * addTypeProperty.
          */
         if (assign)
             cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         else
-            target->addType(cx, TYPE_UNKNOWN);
+            MarkPropertyAccessUnknown(cx, script, pc, target);
+        return;
+    }
+
+    if (type == TYPE_LAZYARGS) {
+        /* Catch cases which will be accounted for by the followEscapingArguments analysis. */
+        if (assign || (id != JSID_VOID && id != id_length(cx)))
+            return;
+
+        if (id == JSID_VOID)
+            MarkPropertyAccessUnknown(cx, script, pc, target);
+        else
+            target->addType(cx, TYPE_INT32);
         return;
     }
 
     TypeObject *object = GetPropertyObject(cx, script, type);
     if (object) {
-        UntrapOpcode untrap(cx, script, pc);
         PropertyAccess(cx, script, pc, object, assign, target, id);
 
         if (!object->unknownProperties() &&
             (JSOp(*pc) == JSOP_CALLPROP || JSOp(*pc) == JSOP_CALLELEM)) {
             JS_ASSERT(!assign);
             TypeSet *types = object->getProperty(cx, id, false);
             if (!types)
                 return;
@@ -1067,17 +1122,17 @@ TypeConstraintCall::newType(JSContext *c
 
     JSScript *callee = function->script;
     unsigned nargs = callee->fun->nargs;
 
     if (!callee->ensureTypeArray(cx))
         return;
 
     /* Analyze the function if we have not already done so. */
-    if (!callee->analyzed) {
+    if (!callee->ranInference) {
         ScriptAnalysis *calleeAnalysis = callee->analysis(cx);
         if (!calleeAnalysis) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
         calleeAnalysis->analyzeTypes(cx);
     }
 
@@ -1376,16 +1431,18 @@ GetValueTypeFromTypeFlags(TypeFlags flag
       case TYPE_FLAG_BOOLEAN:
         return JSVAL_TYPE_BOOLEAN;
       case TYPE_FLAG_INT32:
         return JSVAL_TYPE_INT32;
       case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE):
         return JSVAL_TYPE_DOUBLE;
       case TYPE_FLAG_STRING:
         return JSVAL_TYPE_STRING;
+      case TYPE_FLAG_LAZYARGS:
+        return JSVAL_TYPE_MAGIC;
       default:
         return JSVAL_TYPE_UNKNOWN;
     }
 }
 
 JSValueType
 TypeSet::getKnownTypeTag(JSContext *cx)
 {
@@ -1536,29 +1593,76 @@ TypeSet::HasObjectFlags(JSContext *cx, T
     if (!elementTypes)
         return true;
     elementTypes->add(cx,
         ArenaNew<TypeConstraintFreezeObjectFlags>(cx->compartment->pool,
                                                   cx->compartment->types.compiledScript, flags), false);
     return false;
 }
 
+void
+FixLazyArguments(JSContext *cx, JSScript *script)
+{
+#ifdef JS_METHODJIT
+    mjit::ExpandInlineFrames(cx, FRAME_EXPAND_ALL);
+#endif
+
+    /* :FIXME: handle OOM at calls here. */
+    ScriptAnalysis *analysis = script->analysis(cx);
+    if (analysis && !analysis->ranBytecode())
+        analysis->analyzeBytecode(cx);
+    if (!analysis || analysis->OOM())
+        return;
+
+    for (AllFramesIter iter(cx); !iter.done(); ++iter) {
+        StackFrame *fp = iter.fp();
+        if (fp->isScriptFrame() && fp->script() == script) {
+            JSInlinedSite *inline_;
+            jsbytecode *pc = fp->pc(cx, NULL, &inline_);
+            JS_ASSERT(!inline_);
+
+            /*
+             * Check locals and stack slots, assignment to individual arguments
+             * is treated as an escape on the arguments.
+             */
+            Value *sp = fp->base() + analysis->getCode(pc).stackDepth;
+            for (Value *vp = fp->slots(); vp < sp; vp++) {
+                if (vp->isMagicCheck(JS_LAZY_ARGUMENTS)) {
+                    if (!js_GetArgsValue(cx, fp, vp)) {
+                        /* FIXME */
+                    }
+                }
+            }
+        }
+    }
+}
+
 static inline void
 ObjectStateChange(JSContext *cx, TypeObject *object, bool markingUnknown, bool force)
 {
     if (object->unknownProperties())
         return;
 
     /* All constraints listening to state changes are on the element types. */
     TypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false);
     if (!elementTypes)
         return;
     if (markingUnknown) {
+        JSScript *fixArgsScript = NULL;
+        if (!(object->flags & OBJECT_FLAG_CREATED_ARGUMENTS) && object->isFunction) {
+            TypeFunction *fun = object->asFunction();
+            if (fun->script && fun->script->usedLazyArgs)
+                fixArgsScript = fun->script;
+        }
+
         /* Mark as unknown after getting the element types, to avoid assertion. */
         object->flags = OBJECT_FLAG_UNKNOWN_MASK;
+
+        if (fixArgsScript)
+            FixLazyArguments(cx, fixArgsScript);
     }
 
     TypeConstraint *constraint = elementTypes->constraintList;
     while (constraint) {
         constraint->newObjectState(cx, object, force);
         constraint = constraint->next;
     }
 }
@@ -2030,17 +2134,17 @@ TypeCompartment::dynamicPush(JSContext *
         setPendingNukeTypes(cx);
         return;
     }
     script->addIntermediateType(result);
 
     if (script->hasAnalysis() && script->analysis(cx)->ranInference()) {
         TypeSet *pushed = script->analysis(cx)->pushedTypes(offset, 0);
         pushed->addType(cx, type);
-    } else if (script->analyzed) {
+    } else if (script->ranInference) {
         /* Any new dynamic result triggers reanalysis and recompilation. */
         ScriptAnalysis *analysis = script->analysis(cx);
         if (!analysis) {
             setPendingNukeTypes(cx);
             return;
         }
         analysis->analyzeTypes(cx);
     }
@@ -2749,18 +2853,28 @@ TypeObject::addDefiniteProperties(JSCont
 }
 
 void
 TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
 {
     JS_ASSERT(cx->compartment->activeInference);
     JS_ASSERT((this->flags & flags) != flags);
 
+    JSScript *fixArgsScript = NULL;
+    if ((flags & ~this->flags & OBJECT_FLAG_CREATED_ARGUMENTS) && isFunction) {
+        TypeFunction *fun = asFunction();
+        if (fun->script && fun->script->usedLazyArgs)
+            fixArgsScript = fun->script;
+    }
+
     this->flags |= flags;
 
+    if (fixArgsScript)
+        FixLazyArguments(cx, fixArgsScript);
+
     InferSpew(ISpewOps, "%s: setFlags %u", name(), flags);
 
     ObjectStateChange(cx, this, false, false);
 }
 
 void
 TypeObject::markUnknown(JSContext *cx)
 {
@@ -3415,24 +3529,30 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
             types->addArith(cx, script, types);
             types->addSubset(cx, script, &pushed[0]);
         } else {
             pushed[0].addType(cx, TYPE_UNKNOWN);
         }
         break;
       }
 
-      case JSOP_ARGSUB:
-        pushed[0].addType(cx, TYPE_UNKNOWN);
+      case JSOP_ARGUMENTS: {
+        /* Compute a precise type only when we know the arguments won't escape. */
+        TypeObject *funType = script->fun->getType();
+        if (funType->unknownProperties() || funType->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS)) {
+            pushed[0].addType(cx, TYPE_UNKNOWN);
+            break;
+        }
+        TypeSet *prop = funType->getProperty(cx, JSID_VOID, false);
+        if (!prop)
+            break;
+        prop->addLazyArguments(cx, script, &pushed[0]);
+        pushed[0].addType(cx, TYPE_LAZYARGS);
         break;
-
-      case JSOP_ARGUMENTS:
-      case JSOP_ARGCNT:
-        pushed[0].addType(cx, TYPE_UNKNOWN);
-        break;
+      }
 
       case JSOP_SETPROP:
       case JSOP_SETMETHOD: {
         jsid id = GetAtomId(cx, script, pc, 0);
         poppedTypes(pc, 1)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), id);
         poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
       }
@@ -3881,27 +4001,27 @@ ScriptAnalysis::analyzeTypes(JSContext *
             return;
     }
 
     if (!script->ensureTypeArray(cx)) {
         setOOM(cx);
         return;
     }
 
-    if (script->analyzed) {
+    if (script->ranInference) {
         /*
          * Reanalyzing this script after discarding from GC.
          * Discard/recompile any JIT code for this script,
          * to preserve invariant in TypeConstraintCondensed.
          */
         cx->compartment->types.addPendingRecompile(cx, script);
     }
 
     /* Future OOM failures need to setPendingNukeTypes. */
-    script->analyzed = true;
+    script->ranInference = true;
 
     /*
      * Set this early to avoid reentrance. Any failures are OOMs, and will nuke
      * all types in the compartment.
      */
     ranInference_ = true;
 
     if (script->calledWithNew)
@@ -3941,16 +4061,136 @@ ScriptAnalysis::analyzeTypes(JSContext *
      * the script either because we ran the interpreter some before analyzing
      * or because we are reanalyzing after a GC.
      */
     TypeIntermediate *result = script->intermediateTypes;
     while (result) {
         result->replay(cx, script);
         result = result->next;
     }
+
+    if (!script->usesArguments)
+        return;
+
+    /*
+     * Do additional analysis to determine whether the arguments object in the
+     * script can escape.
+     */
+
+    if (script->fun->getType()->hasAnyFlags(types::OBJECT_FLAG_CREATED_ARGUMENTS))
+        return;
+
+    /*
+     * Note: don't check for strict mode code here, even though arguments
+     * accesses in such scripts will always be deoptimized. These scripts can
+     * have a JSOP_ARGUMENTS in their prologue which the usesArguments check
+     * above does not account for. We filter in the interpreter and JITs
+     * themselves.
+     */
+    if (script->fun->isHeavyweight() || cx->compartment->debugMode) {
+        cx->markTypeObjectFlags(script->fun->getType(),
+                                types::OBJECT_FLAG_CREATED_ARGUMENTS);
+        return;
+    }
+
+    offset = 0;
+    while (offset < script->length) {
+        Bytecode *code = maybeCode(offset);
+        jsbytecode *pc = script->code + offset;
+
+        if (code && JSOp(*pc) == JSOP_ARGUMENTS) {
+            Vector<SSAValue> seen(cx);
+            if (!followEscapingArguments(cx, SSAValue::PushedValue(offset, 0), &seen)) {
+                cx->markTypeObjectFlags(script->fun->getType(),
+                                        types::OBJECT_FLAG_CREATED_ARGUMENTS);
+                return;
+            }
+        }
+
+        offset += GetBytecodeLength(pc);
+    }
+
+    /*
+     * The VM is now free to use the arguments in this script lazily. If we end
+     * up creating an arguments object for the script in the future or regard
+     * the arguments as escaping, we need to walk the stack and replace lazy
+     * arguments objects with actual arguments objects.
+     */
+    script->usedLazyArgs = true;
+}
+
+bool
+ScriptAnalysis::followEscapingArguments(JSContext *cx, const SSAValue &v, Vector<SSAValue> *seen)
+{
+    /*
+     * trackUseChain is false for initial values of variables, which
+     * cannot hold the script's arguments object.
+     */
+    if (!trackUseChain(v))
+        return true;
+
+    for (unsigned i = 0; i < seen->length(); i++) {
+        if (v.equals((*seen)[i]))
+            return true;
+    }
+    if (!seen->append(v)) {
+        cx->compartment->types.setPendingNukeTypes(cx);
+        return false;
+    }
+
+    SSAUseChain *use = useChain(v);
+    while (use) {
+        if (!followEscapingArguments(cx, use, seen))
+            return false;
+        use = use->next;
+    }
+
+    return true;
+}
+
+bool
+ScriptAnalysis::followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector<SSAValue> *seen)
+{
+    if (!use->popped) {
+        for (unsigned i = 0; i < use->u.phi->length; i++) {
+            const SSAValue &v = use->u.phi->options[i];
+            if (!followEscapingArguments(cx, v, seen))
+                return false;
+        }
+        return true;
+    }
+
+    jsbytecode *pc = script->code + use->offset;
+    uint32 which = use->u.which;
+
+    /* Allow GETELEM and LENGTH on arguments objects that don't escape. */
+
+    /*
+     * Note: if the element index is not an integer we will mark the arguments
+     * as escaping at the access site.
+     */
+    if (JSOp(*pc) == JSOP_GETELEM && which == 1)
+        return true;
+
+    if (JSOp(*pc) == JSOP_LENGTH)
+        return true;
+
+    /* Allow assignments to non-closed locals (but not arguments). */
+
+    if (JSOp(*pc) == JSOP_SETLOCAL) {
+        uint32 slot = GetBytecodeSlot(script, pc);
+        if (slotEscapes(slot))
+            return false;
+        return followEscapingArguments(cx, SSAValue::WrittenVar(slot, use->offset), seen);
+    }
+
+    if (JSOp(*pc) == JSOP_GETLOCAL)
+        return followEscapingArguments(cx, SSAValue::PushedValue(use->offset, 0), seen);
+
+    return false;
 }
 
 void
 ScriptAnalysis::analyzeTypesNew(JSContext *cx)
 {
     JS_ASSERT(script->calledWithNew && script->fun);
 
     /*
@@ -4324,17 +4564,17 @@ ScriptAnalysis::printTypes(JSContext *cx
             TypeSet *types = pushedTypes(offset, i);
 
             if (types->unknown()) {
                 compartment->typeCountOver++;
                 continue;
             }
 
             unsigned typeCount = types->getObjectCount() ? 1 : 0;
-            for (jstype type = TYPE_UNDEFINED; type <= TYPE_STRING; type++) {
+            for (jstype type = TYPE_UNDEFINED; type < TYPE_UNKNOWN; type++) {
                 if (types->hasAnyFlag(1 << type))
                     typeCount++;
             }
 
             /*
              * Adjust the type counts for floats: values marked as floats
              * are also marked as ints by the inference, but for counting
              * we don't consider these to be separate types.
@@ -4585,17 +4825,17 @@ JSScript::typeCheckBytecode(JSContext *c
     for (int i = 0; i < defCount; i++) {
         const js::Value &val = sp[-defCount + i];
         TypeSet *types = analysis_->pushedTypes(pc, i);
         if (IgnorePushed(pc, i))
             continue;
 
         jstype type = GetValueType(cx, val);
 
-        if (!TypeSetMatches(cx, types, type)) {
+        if (!types::TypeMatches(cx, types, type)) {
             TypeFailure(cx, "Missing type at #%u:%05u pushed %u: %s",
                                    id(), pc - code, i, TypeString(type));
         }
 
         if (TypeIsObject(type)) {
             JS_ASSERT(val.isObject());
             JSObject *obj = &val.toObject();
             TypeObject *object = (TypeObject *) type;
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -71,22 +71,23 @@ typedef jsuword jstype;
 
 /* The primitive types. */
 const jstype TYPE_UNDEFINED = 1;
 const jstype TYPE_NULL      = 2;
 const jstype TYPE_BOOLEAN   = 3;
 const jstype TYPE_INT32     = 4;
 const jstype TYPE_DOUBLE    = 5;
 const jstype TYPE_STRING    = 6;
+const jstype TYPE_LAZYARGS  = 7;
 
 /*
  * Aggregate unknown type, could be anything. Typically used when a type set
  * becomes polymorphic, or when accessing an object with unknown properties.
  */
-const jstype TYPE_UNKNOWN = 7;
+const jstype TYPE_UNKNOWN = 8;
 
 /*
  * Test whether a type is an primitive or an object. Object types can be
  * cast into a TypeObject*.
  */
 
 static inline bool
 TypeIsPrimitive(jstype type)
@@ -213,47 +214,48 @@ public:
 /* Coarse flags for the contents of a type set. */
 enum {
     TYPE_FLAG_UNDEFINED = 1 << TYPE_UNDEFINED,
     TYPE_FLAG_NULL      = 1 << TYPE_NULL,
     TYPE_FLAG_BOOLEAN   = 1 << TYPE_BOOLEAN,
     TYPE_FLAG_INT32     = 1 << TYPE_INT32,
     TYPE_FLAG_DOUBLE    = 1 << TYPE_DOUBLE,
     TYPE_FLAG_STRING    = 1 << TYPE_STRING,
+    TYPE_FLAG_LAZYARGS  = 1 << TYPE_LAZYARGS,
 
     TYPE_FLAG_UNKNOWN   = 1 << TYPE_UNKNOWN,
 
     /* Flag for type sets which are cleared on GC. */
-    TYPE_FLAG_INTERMEDIATE_SET    = 0x0100,
+    TYPE_FLAG_INTERMEDIATE_SET    = 0x0200,
 
     /* Flags for type sets which are on object properties. */
 
     /* Whether this property has ever been directly written. */
-    TYPE_FLAG_OWN_PROPERTY        = 0x0200,
+    TYPE_FLAG_OWN_PROPERTY        = 0x0400,
 
     /*
      * Whether the property has ever been deleted or reconfigured to behave
      * differently from a normal native property (e.g. made non-writable or
      * given a scripted getter or setter).
      */
-    TYPE_FLAG_CONFIGURED_PROPERTY = 0x0400,
+    TYPE_FLAG_CONFIGURED_PROPERTY = 0x0800,
 
     /*
      * Whether the property is definitely in a particular inline slot on all
      * objects from which it has not been deleted or reconfigured. Implies
      * OWN_PROPERTY and unlike OWN/CONFIGURED property, this cannot change.
      */
-    TYPE_FLAG_DEFINITE_PROPERTY   = 0x0800,
+    TYPE_FLAG_DEFINITE_PROPERTY   = 0x08000,
 
     /* If the property is definite, mask and shift storing the slot. */
-    TYPE_FLAG_DEFINITE_MASK       = 0xf000,
-    TYPE_FLAG_DEFINITE_SHIFT      = 12,
+    TYPE_FLAG_DEFINITE_MASK       = 0xf0000,
+    TYPE_FLAG_DEFINITE_SHIFT      = 16,
 
     /* Mask of non-type flags on a type set. */
-    TYPE_FLAG_BASE_MASK           = 0xffffff00
+    TYPE_FLAG_BASE_MASK           = 0xffffffff ^ ((TYPE_FLAG_UNKNOWN << 1) - 1)
 };
 typedef uint32 TypeFlags;
 
 /* Bitmask for possible dynamic properties of the JSObjects with some type. */
 enum {
     /*
      * Whether all the properties of this object are unknown. When this object
      * appears in a type set, nothing can be assumed about its contents,
@@ -267,24 +269,27 @@ enum {
      * Whether any objects this represents are not dense arrays. This also
      * includes dense arrays whose length property does not fit in an int32.
      */
     OBJECT_FLAG_NON_DENSE_ARRAY = 1 << 0,
 
     /* Whether any objects this represents are not packed arrays. */
     OBJECT_FLAG_NON_PACKED_ARRAY = 1 << 1,
 
-    /* Whether any objects this represents have had their .arguments accessed. */
-    OBJECT_FLAG_UNINLINEABLE = 1 << 2,
+    /* Whether any represented script has had arguments objects created. */
+    OBJECT_FLAG_CREATED_ARGUMENTS = 1 << 2,
+
+    /* Whether any represented script is considered uninlineable. */
+    OBJECT_FLAG_UNINLINEABLE = 1 << 3,
 
-    /* Whether any objects this represents have an equality hook. */
-    OBJECT_FLAG_SPECIAL_EQUALITY = 1 << 3,
+    /* Whether any objects have an equality hook. */
+    OBJECT_FLAG_SPECIAL_EQUALITY = 1 << 4,
 
-    /* Whether any objects this represents have been iterated over. */
-    OBJECT_FLAG_ITERATED = 1 << 4
+    /* Whether any objects have been iterated over. */
+    OBJECT_FLAG_ITERATED = 1 << 5
 };
 typedef uint32 TypeObjectFlags;
 
 /* Information about the set of types associated with an lvalue. */
 class TypeSet
 {
     /* Flags for the possible coarse types in this set. */
     TypeFlags typeFlags;
@@ -365,16 +370,17 @@ class TypeSet
     void addCall(JSContext *cx, TypeCallsite *site);
     void addArith(JSContext *cx, JSScript *script,
                   TypeSet *target, TypeSet *other = NULL);
     void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
     void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, jstype type);
     void addFilterPrimitives(JSContext *cx, JSScript *script,
                              TypeSet *target, bool onlyNullVoid);
     void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
+    void addLazyArguments(JSContext *cx, JSScript *script, TypeSet *target);
 
     void addBaseSubset(JSContext *cx, TypeObject *object, TypeSet *target);
     bool addCondensed(JSContext *cx, JSScript *script);
 
     /*
      * Make an intermediate type set with the specified debugging name,
      * not embedded in another structure.
      */
@@ -388,16 +394,18 @@ class TypeSet
      */
 
     /* Completely freeze the contents of this type set. */
     void addFreeze(JSContext *cx);
 
     /* Get any type tag which all values in this set must have. */
     JSValueType getKnownTypeTag(JSContext *cx);
 
+    bool isLazyArguments(JSContext *cx) { return getKnownTypeTag(cx) == JSVAL_TYPE_MAGIC; }
+
     /* Whether the type set or a particular object has any of a set of flags. */
     bool hasObjectFlags(JSContext *cx, TypeObjectFlags flags);
     static bool HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags);
 
     /* Watch for slot reallocations on a particular object. */
     static void WatchObjectReallocation(JSContext *cx, JSObject *object);
 
     /*
@@ -779,16 +787,22 @@ struct TypeCompartment
 
     /* Number of scripts in this compartment. */
     unsigned scriptCount;
 
     /* Object to use throughout the compartment as the default type of objects with no prototype. */
     TypeObject typeEmpty;
 
     /*
+     * Placeholder object added in type sets throughout the compartment to
+     * represent lazy arguments objects.
+     */
+    TypeObject typeLazyArguments;
+
+    /*
      * Bit set if all current types must be marked as unknown, and all scripts
      * recompiled. Caused by OOM failure within inference operations.
      */
     bool pendingNukeTypes;
 
     /*
      * Whether type sets have been nuked, and all future type sets should be as well.
      * This is not strictly necessary to do, but avoids thrashing from repeated
@@ -898,16 +912,23 @@ enum SpewChannel {
     SPEW_COUNT
 };
 
 #ifdef DEBUG
 
 void InferSpew(SpewChannel which, const char *fmt, ...);
 const char * TypeString(jstype type);
 
+/*
+ * Check that a type set contains value. Unlike TypeSet::hasType, this returns
+ * true if type has had its prototype mutated and another object with unknown
+ * properties is in the type set.
+ */
+bool TypeMatches(JSContext *cx, TypeSet *types, jstype type);
+
 /* Check that the type property for id in obj contains value. */
 bool TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value);
 
 #else
 
 inline void InferSpew(SpewChannel which, const char *fmt, ...) {}
 inline const char * TypeString(jstype type) { return NULL; }
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -68,16 +68,24 @@ GetValueType(JSContext *cx, const Value 
       case JSVAL_TYPE_UNDEFINED:
         return TYPE_UNDEFINED;
       case JSVAL_TYPE_BOOLEAN:
         return TYPE_BOOLEAN;
       case JSVAL_TYPE_STRING:
         return TYPE_STRING;
       case JSVAL_TYPE_NULL:
         return TYPE_NULL;
+      case JSVAL_TYPE_MAGIC:
+        switch (val.whyMagic()) {
+          case JS_LAZY_ARGUMENTS:
+            return TYPE_LAZYARGS;
+          default:
+            JS_NOT_REACHED("Unknown value");
+            return (jstype) 0;
+        }
       case JSVAL_TYPE_OBJECT: {
         JSObject *obj = &val.toObject();
         JS_ASSERT(obj->type);
         return (jstype) obj->type;
       }
       default:
         JS_NOT_REACHED("Unknown value");
         return (jstype) 0;
@@ -747,17 +755,17 @@ JSScript::typeSetNewCalled(JSContext *cx
 
     /*
      * Determining the 'this' type used when the script is invoked with 'new'
      * happens during the script's prologue, so we don't try to pick it up from
      * dynamic calls. Instead, generate constraints modeling the construction
      * of 'this' when the script is analyzed or reanalyzed after an invoke with 'new',
      * and if 'new' is first invoked after the script has already been analyzed.
      */
-    if (analyzed) {
+    if (ranInference) {
         /* Regenerate types for the function. */
         js::types::AutoEnterTypeInference enter(cx);
         js::analyze::ScriptAnalysis *analysis = this->analysis(cx);
         if (!analysis)
             return;
         analysis->analyzeTypesNew(cx);
     }
 }
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -4112,16 +4112,20 @@ BEGIN_CASE(JSOP_LENGTH)
         Value *vp = &regs.sp[-1];
 
         if (op == JSOP_LENGTH) {
             /* Optimize length accesses on strings, arrays, and arguments. */
             if (vp->isString()) {
                 rval = Int32Value(vp->toString()->length());
                 break;
             }
+            if (vp->isMagic(JS_LAZY_ARGUMENTS)) {
+                rval = Int32Value(regs.fp()->numActualArgs());
+                break;
+            }
             if (vp->isObject()) {
                 JSObject *obj = &vp->toObject();
                 if (obj->isArray()) {
                     jsuint length = obj->getArrayLength();
                     rval = NumberValue(length);
                     break;
                 }
 
@@ -4454,16 +4458,29 @@ BEGIN_CASE(JSOP_GETELEM)
             regs.sp--;
             regs.sp[-1].setString(str);
             script->typeMonitor(cx, regs.pc, regs.sp[-1]);
             len = JSOP_GETELEM_LENGTH;
             DO_NEXT_OP(len);
         }
     }
 
+    if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
+        if (rref.isInt32() && size_t(rref.toInt32()) < regs.fp()->numActualArgs()) {
+            regs.sp--;
+            regs.sp[-1] = regs.fp()->canonicalActualArg(rref.toInt32());
+            script->typeMonitor(cx, regs.pc, regs.sp[-1]);
+            len = JSOP_GETELEM_LENGTH;
+            DO_NEXT_OP(len);
+        }
+        cx->markTypeObjectFlags(script->fun->getType(),
+                                types::OBJECT_FLAG_CREATED_ARGUMENTS);
+        JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
+    }
+
     JSObject *obj;
     VALUE_TO_OBJECT(cx, &lref, obj);
 
     const Value *copyFrom;
     Value rval;
     jsid id;
     if (rref.isInt32()) {
         int32_t i = rref.toInt32();
@@ -5152,18 +5169,34 @@ BEGIN_CASE(JSOP_TRAP)
     op = (JSOp) rval.toInt32();
     JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
     DO_OP();
 }
 
 BEGIN_CASE(JSOP_ARGUMENTS)
 {
     Value rval;
-    if (!js_GetArgsValue(cx, regs.fp(), &rval))
-        goto error;
+    if (cx->typeInferenceEnabled() && !script->strictModeCode) {
+        analyze::ScriptAnalysis *analysis = script->analysis(cx);
+        if (analysis && !analysis->ranInference()) {
+            AutoEnterTypeInference enter(cx);
+            analysis->analyzeTypes(cx);
+        }
+        if (!analysis || analysis->OOM())
+            goto error;
+        if (script->fun->getType()->hasAnyFlags(types::OBJECT_FLAG_CREATED_ARGUMENTS)) {
+            if (!js_GetArgsValue(cx, regs.fp(), &rval))
+                goto error;
+        } else {
+            rval = MagicValue(JS_LAZY_ARGUMENTS);
+        }
+    } else {
+        if (!js_GetArgsValue(cx, regs.fp(), &rval))
+            goto error;
+    }
     PUSH_COPY(rval);
 }
 END_CASE(JSOP_ARGUMENTS)
 
 BEGIN_CASE(JSOP_ARGSUB)
 {
     jsid id = INT_TO_JSID(GET_ARGNO(regs.pc));
     Value rval;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -460,17 +460,18 @@ struct JSScript {
     bool            warnedAboutTwoArgumentEval:1; /* have warned about use of
                                                      obsolete eval(s, o) in
                                                      this script */
     bool            hasSingletons:1;  /* script has singleton objects */
     bool            isActiveEval:1;   /* script came from eval(), and is still active */
     bool            isCachedEval:1;   /* script came from eval(), and is in eval cache */
     bool            isUncachedEval:1; /* script came from EvaluateScript */
     bool            calledWithNew:1;  /* script has been called using 'new' */
-    bool            analyzed:1;       /* script has been analyzed by type inference */
+    bool            usedLazyArgs:1;   /* script has used lazy arguments at some point */
+    bool            ranInference:1;   /* script has been analyzed by type inference */
 #ifdef JS_METHODJIT
     bool            debugMode:1;      /* script was compiled in debug mode */
     bool            singleStepMode:1; /* compile script in single-step mode */
     bool            failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
 #endif
 
     jsbytecode      *main;      /* main entry point, after predef'ing prolog */
     JSAtomMap       atomMap;    /* maps immediate index to literal struct */
--- a/js/src/jsval.h
+++ b/js/src/jsval.h
@@ -261,16 +261,17 @@ typedef enum JSWhyMagic
                                   * to js_Enumerate, which really means the object can be
                                   * enumerated like a native object. */
     JS_NO_ITER_VALUE,            /* there is not a pending iterator value */
     JS_GENERATOR_CLOSING,        /* exception value thrown when closing a generator */
     JS_NO_CONSTANT,              /* compiler sentinel value */
     JS_THIS_POISON,              /* used in debug builds to catch tracing errors */
     JS_ARG_POISON,               /* used in debug builds to catch tracing errors */
     JS_SERIALIZE_NO_NODE,        /* an empty subnode in the AST serializer */
+    JS_LAZY_ARGUMENTS,           /* lazy arguments value on the stack */
     JS_GENERIC_MAGIC             /* for local use */
 } JSWhyMagic;
 
 #ifdef __cplusplus
 class                       JSString;
 class                       JSFlatString;
 #else
 typedef struct JSString     JSString;
--- a/js/src/jsvalue.h
+++ b/js/src/jsvalue.h
@@ -534,16 +534,21 @@ class Value
     }
 
     JS_ALWAYS_INLINE
     bool isMagic(JSWhyMagic why) const {
         JS_ASSERT_IF(isMagic(), data.s.payload.why == why);
         return JSVAL_IS_MAGIC_IMPL(data);
     }
 
+    JS_ALWAYS_INLINE
+    bool isMagicCheck(JSWhyMagic why) const {
+        return isMagic() && data.s.payload.why == why;
+    }
+
 #if JS_BITS_PER_WORD == 64
     JS_ALWAYS_INLINE
     bool hasPtrPayload() const {
         return data.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_PTR_PAYLOAD_SET;
     }
 #endif
 
     JS_ALWAYS_INLINE
@@ -552,23 +557,21 @@ class Value
     }
 
     JS_ALWAYS_INLINE
     int32 gcKind() const {
         JS_ASSERT(isMarkable());
         return JSVAL_TRACE_KIND_IMPL(data);
     }
 
-#ifdef DEBUG
     JS_ALWAYS_INLINE
     JSWhyMagic whyMagic() const {
         JS_ASSERT(isMagic());
         return data.s.payload.why;
     }
-#endif
 
     /*** Comparison ***/
 
     JS_ALWAYS_INLINE
     bool operator==(const Value &rhs) const {
         return data.asBits == rhs.data.asBits;
     }
 
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -780,16 +780,30 @@ static const JSC::MacroAssembler::Regist
 
     void bumpKey(Int32Key &key, int32 delta) {
         if (key.isConstant())
             key.index_ += delta;
         else
             add32(Imm32(delta), key.reg());
     }
 
+    void loadFrameActuals(JSFunction *fun, RegisterID reg) {
+        /* Bias for the case where there was an arguments overflow. */
+        load32(Address(JSFrameReg, StackFrame::offsetOfArgs()), reg);
+        add32(Imm32(fun->nargs + 2), reg);
+        Jump overflowArgs = branchTest32(Assembler::NonZero,
+                                         Address(JSFrameReg, StackFrame::offsetOfFlags()),
+                                         Imm32(StackFrame::OVERFLOW_ARGS));
+        move(Imm32(fun->nargs), reg);
+        overflowArgs.linkTo(label(), this);
+        lshift32(Imm32(3), reg);
+        neg32(reg);
+        addPtr(JSFrameReg, reg);
+    }
+
     void loadObjClass(RegisterID objReg, RegisterID destReg) {
         loadPtr(Address(objReg, offsetof(JSObject, clasp)), destReg);
     }
 
     Jump testClass(Condition cond, RegisterID claspReg, js::Class *clasp) {
         return branchPtr(cond, claspReg, ImmPtr(clasp));
     }
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -782,16 +782,32 @@ mjit::Compiler::generatePrologue()
             RegisterID t0 = Registers::ReturnReg;
             Jump hasScope = masm.branchTest32(Assembler::NonZero,
                                               FrameFlagsAddress(), Imm32(StackFrame::HAS_SCOPECHAIN));
             masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(script->fun)), t0);
             masm.loadPtr(Address(t0, offsetof(JSObject, parent)), t0);
             masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain()));
             hasScope.linkTo(masm.label(), &masm);
         }
+
+        if (outerScript->usesArguments && !script->fun->isHeavyweight()) {
+            /*
+             * Make sure that fp->args.nactual is always coherent. This may be
+             * inspected directly by JIT code, and is not guaranteed to be
+             * correct if the UNDERFLOW and OVERFLOW flags are not set.
+             */
+            Jump hasArgs = masm.branchTest32(Assembler::NonZero, FrameFlagsAddress(),
+                                             Imm32(StackFrame::OVERRIDE_ARGS |
+                                                   StackFrame::UNDERFLOW_ARGS |
+                                                   StackFrame::OVERFLOW_ARGS |
+                                                   StackFrame::HAS_ARGS_OBJ));
+            masm.store32(Imm32(script->fun->nargs),
+                         Address(JSFrameReg, StackFrame::offsetOfArgs()));
+            hasArgs.linkTo(masm.label(), &masm);
+        }
     }
 
     if (isConstructing)
         constructThis();
 
     if (debugMode() || Probes::callTrackingActive(cx))
         INLINE_STUBCALL(stubs::ScriptDebugPrologue, REJOIN_RESUME);
 
@@ -1614,21 +1630,26 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_ARGUMENTS)
             /*
              * For calls of the form 'f.apply(x, arguments)' we can avoid
              * creating an args object by having ic::SplatApplyArgs pull
              * directly from the stack. To do this, we speculate here that
              * 'apply' actually refers to js_fun_apply. If this is not true,
              * the slow path in JSOP_FUNAPPLY will create the args object.
              */
-            if (canUseApplyTricks())
+            if (canUseApplyTricks()) {
                 applyTricks = LazyArgsObj;
-            else
-                jsop_arguments();
-            pushSyncedEntry(0);
+                pushSyncedEntry(0);
+            } else if (cx->typeInferenceEnabled() && !script->strictModeCode &&
+                       !script->fun->getType()->hasAnyFlags(types::OBJECT_FLAG_CREATED_ARGUMENTS)) {
+                frame.push(MagicValue(JS_LAZY_ARGUMENTS));
+            } else {
+                jsop_arguments(REJOIN_FALLTHROUGH);
+                pushSyncedEntry(0);
+            }
           END_CASE(JSOP_ARGUMENTS)
 
           BEGIN_CASE(JSOP_FORARG)
           {
             uint32 arg = GET_SLOTNO(PC);
             iterNext();
             frame.storeArg(arg, true);
             frame.pop();
@@ -3396,17 +3417,17 @@ mjit::Compiler::inlineCallHelper(uint32 
 
     bool newType = callingNew && cx->typeInferenceEnabled() && types::UseNewType(cx, script, PC);
 
 #ifdef JS_MONOIC
     if (debugMode() || newType) {
 #endif
         if (applyTricks == LazyArgsObj) {
             /* frame.pop() above reset us to pre-JSOP_ARGUMENTS state */
-            jsop_arguments();
+            jsop_arguments(REJOIN_RESUME);
             frame.pushSynced(JSVAL_TYPE_UNKNOWN);
         }
         emitUncachedCall(callImmArgc, callingNew);
         applyTricks = NoApplyTricks;
         return true;
 #ifdef JS_MONOIC
     }
 
@@ -4165,62 +4186,71 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
             masm.loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), str);
             masm.urshift32(Imm32(JSString::LENGTH_SHIFT), str);
             frame.pop();
             frame.pushTypedPayload(JSVAL_TYPE_INT32, str);
         }
         return true;
     }
 
-    /* If the incoming type will never PIC, take slow path. */
-    if (top->isNotType(JSVAL_TYPE_OBJECT)) {
-        jsop_getprop_slow(atom, usePropCache);
-        return true;
-    }
-
-    frame.forgetMismatchedObject(top);
-
     if (JSOp(*PC) == JSOP_LENGTH && cx->typeInferenceEnabled() && !hasTypeBarriers(PC)) {
-        /*
-         * Check if this is an array we can make a loop invariant entry for.
-         * This will fail for objects which are not definitely dense arrays.
-         */
+        /* Check if this is an array we can make a loop invariant entry for. */
         if (loop && loop->generatingInvariants()) {
             CrossSSAValue topv(a->inlineIndex, analysis->poppedValue(PC, 0));
             FrameEntry *fe = loop->invariantLength(topv);
             if (fe) {
                 frame.learnType(fe, JSVAL_TYPE_INT32, false);
                 frame.pop();
                 frame.pushCopyOf(fe);
                 return true;
             }
         }
 
+        types::TypeSet *types = analysis->poppedTypes(PC, 0);
+
         /*
          * Check if we are accessing the 'length' property of a known dense array.
          * Note that if the types are known to indicate dense arrays, their lengths
          * must fit in an int32.
          */
-        types::TypeSet *types = analysis->poppedTypes(PC, 0);
         if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY)) {
             bool isObject = top->isTypeKnown();
             if (!isObject) {
                 Jump notObject = frame.testObject(Assembler::NotEqual, top);
                 stubcc.linkExit(notObject, Uses(1));
                 stubcc.leave();
                 OOL_STUBCALL(stubs::GetProp, rejoin);
             }
             RegisterID reg = frame.tempRegForData(top);
             frame.pop();
             frame.push(Address(reg, offsetof(JSObject, privateData)), JSVAL_TYPE_INT32);
             if (!isObject)
                 stubcc.rejoin(Changes(1));
             return true;
         }
-    }
+
+        /*
+         * Check if we are accessing the 'length' of the lazy arguments for the
+         * current frame. No actual arguments object has ever been constructed
+         * for the script, so we can go straight to nactual.
+         */
+        if (types->isLazyArguments(cx)) {
+            frame.pop();
+            frame.push(Address(JSFrameReg, StackFrame::offsetOfArgs()), JSVAL_TYPE_INT32);
+            return true;
+        }
+    }
+
+    /* If the incoming type will never PIC, take slow path. */
+    if (top->isNotType(JSVAL_TYPE_OBJECT)) {
+        jsop_getprop_slow(atom, usePropCache);
+        return true;
+    }
+
+    frame.forgetMismatchedObject(top);
 
     /* Check if this is a property access we can make a loop invariant entry for. */
     if (loop && loop->generatingInvariants() && !hasTypeBarriers(PC)) {
         CrossSSAValue topv(a->inlineIndex, analysis->poppedValue(PC, 0));
         FrameEntry *fe = loop->invariantProperty(topv, ATOM_TO_JSID(atom));
         if (fe) {
             if (knownType != JSVAL_TYPE_UNKNOWN && knownType != JSVAL_TYPE_DOUBLE)
                 frame.learnType(fe, knownType, false);
@@ -6302,20 +6332,20 @@ mjit::Compiler::emitEval(uint32 argc)
     prepareStubCall(Uses(argc + 2));
     masm.move(Imm32(argc), Registers::ArgReg1);
     INLINE_STUBCALL(stubs::Eval, REJOIN_FALLTHROUGH);
     frame.popn(argc + 2);
     pushSyncedEntry(0);
 }
 
 void
-mjit::Compiler::jsop_arguments()
+mjit::Compiler::jsop_arguments(RejoinState rejoin)
 {
     prepareStubCall(Uses(0));
-    INLINE_STUBCALL(stubs::Arguments, REJOIN_NONE);
+    INLINE_STUBCALL(stubs::Arguments, rejoin);
 }
 
 bool
 mjit::Compiler::jsop_newinit()
 {
     bool isArray;
     unsigned count = 0;
     JSObject *baseobj = NULL;
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -649,17 +649,17 @@ class Compiler : public BaseCompiler
     bool jsop_callprop_generic(JSAtom *atom);
     bool jsop_callprop_dispatch(JSAtom *atom);
     bool jsop_instanceof();
     void jsop_name(JSAtom *atom, JSValueType type);
     bool jsop_xname(JSAtom *atom);
     void enterBlock(JSObject *obj);
     void leaveBlock();
     void emitEval(uint32 argc);
-    void jsop_arguments();
+    void jsop_arguments(RejoinState rejoin);
     bool jsop_tableswitch(jsbytecode *pc);
     void jsop_forprop(JSAtom *atom);
     void jsop_forname(JSAtom *atom);
     void jsop_forgname(JSAtom *atom);
 
     /* Fast arithmetic. */
     bool jsop_binary(JSOp op, VoidStub stub, JSValueType type, types::TypeSet *typeSet);
     void jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub,
@@ -702,16 +702,17 @@ class Compiler : public BaseCompiler
     bool jsop_newinit();
     void jsop_initmethod();
     void jsop_initprop();
     void jsop_initelem();
     void jsop_setelem_dense();
     bool jsop_setelem(bool popGuaranteed);
     bool jsop_getelem(bool isCall);
     void jsop_getelem_dense(bool isPacked);
+    void jsop_getelem_args();
     bool isCacheableBaseAndIndex(FrameEntry *obj, FrameEntry *id);
     void jsop_stricteq(JSOp op);
     bool jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
     bool jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
     void jsop_pos();
 
     static inline Assembler::Condition
     GetCompareCondition(JSOp op, JSOp fused)
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -1105,17 +1105,17 @@ mjit::Compiler::jsop_setelem_dense()
     // invariant and never access the object itself.
     RegisterID slotsReg;
     analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 2));
     analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 1));
     bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
         loop->hoistArrayLengthCheck(objv, indexv);
 
     if (hoisted) {
-        FrameEntry *slotsFe = loop->invariantSlots(objv);
+        FrameEntry *slotsFe = loop->invariantArraySlots(objv);
         slotsReg = frame.tempRegForData(slotsFe);
 
         frame.unpinEntry(vr);
         if (pinKey)
             frame.unpinReg(key.reg());
     } else {
         // Get a register for the object which we can clobber.
         RegisterID objReg;
@@ -1370,18 +1370,16 @@ mjit::Compiler::jsop_setelem(bool popGua
 #endif
 
     return true;
 }
 
 static inline bool
 IsCacheableGetElem(FrameEntry *obj, FrameEntry *id)
 {
-    if (obj->isTypeKnown() && obj->getKnownType() != JSVAL_TYPE_OBJECT)
-        return false;
     if (id->isTypeKnown() &&
         !(id->getKnownType() == JSVAL_TYPE_INT32
 #if defined JS_POLYIC
           || id->getKnownType() == JSVAL_TYPE_STRING
 #endif
          )) {
         return false;
     }
@@ -1430,17 +1428,17 @@ mjit::Compiler::jsop_getelem_dense(bool 
     analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
     bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
         loop->hoistArrayLengthCheck(objv, indexv);
 
     // Get a register with either the object or its slots, depending on whether
     // we are hoisting the bounds check.
     RegisterID baseReg;
     if (hoisted) {
-        FrameEntry *slotsFe = loop->invariantSlots(objv);
+        FrameEntry *slotsFe = loop->invariantArraySlots(objv);
         baseReg = frame.tempRegForData(slotsFe);
     } else {
         baseReg = frame.tempRegForData(obj);
     }
     frame.pinReg(baseReg);
 
     Int32Key key = id->isConstant()
                  ? Int32Key::FromConstant(id->getValue().toInt32())
@@ -1516,45 +1514,121 @@ mjit::Compiler::jsop_getelem_dense(bool 
         else
             stubcc.masm.loadValueAsComponents(UndefinedValue(), typeReg.reg(), dataReg);
         stubcc.linkRejoin(stubcc.masm.jump());
     }
 
     finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
 }
 
+void
+mjit::Compiler::jsop_getelem_args()
+{
+    FrameEntry *id = frame.peek(-1);
+
+    // Test for integer index.
+    if (!id->isTypeKnown()) {
+        Jump guard = frame.testInt32(Assembler::NotEqual, id);
+        stubcc.linkExit(guard, Uses(2));
+    }
+
+    // Allocate registers.
+
+    analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
+    bool hoistedLength = loop && id->isType(JSVAL_TYPE_INT32) &&
+        loop->hoistArgsLengthCheck(indexv);
+    FrameEntry *actualsFe = loop ? loop->invariantArguments() : NULL;
+
+    Int32Key key = id->isConstant()
+                 ? Int32Key::FromConstant(id->getValue().toInt32())
+                 : Int32Key::FromRegister(frame.tempRegForData(id));
+    if (!key.isConstant())
+        frame.pinReg(key.reg());
+
+    RegisterID dataReg = frame.allocReg();
+    RegisterID typeReg = frame.allocReg();
+
+    if (!key.isConstant())
+        frame.unpinReg(key.reg());
+
+    // Guard on nactual.
+    if (!hoistedLength) {
+        Address nactualAddr(JSFrameReg, StackFrame::offsetOfArgs());
+        MaybeJump rangeGuard;
+        if (key.isConstant()) {
+            JS_ASSERT(key.index() >= 0);
+            rangeGuard = masm.branch32(Assembler::BelowOrEqual, nactualAddr, Imm32(key.index()));
+        } else {
+            rangeGuard = masm.branch32(Assembler::BelowOrEqual, nactualAddr, key.reg());
+        }
+        stubcc.linkExit(rangeGuard.get(), Uses(2));
+    }
+
+    RegisterID actualsReg;
+    if (actualsFe) {
+        actualsReg = frame.tempRegForData(actualsFe);
+    } else {
+        actualsReg = dataReg;
+        masm.loadFrameActuals(outerScript->fun, actualsReg);
+    }
+
+    if (key.isConstant()) {
+        Address arg(actualsReg, key.index() * sizeof(Value));
+        masm.loadValueAsComponents(arg, typeReg, dataReg);
+    } else {
+        JS_ASSERT(key.reg() != dataReg);
+        BaseIndex arg(actualsReg, key.reg(), masm.JSVAL_SCALE);
+        masm.loadValueAsComponents(arg, typeReg, dataReg);
+    }
+
+    stubcc.leave();
+    OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
+
+    frame.popn(2);
+    frame.pushRegs(typeReg, dataReg, knownPushedType(0));
+    BarrierState barrier = testBarrier(typeReg, dataReg, false);
+
+    stubcc.rejoin(Changes(2));
+
+    finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
+}
+
 bool
 mjit::Compiler::jsop_getelem(bool isCall)
 {
     FrameEntry *obj = frame.peek(-2);
     FrameEntry *id = frame.peek(-1);
 
     if (!IsCacheableGetElem(obj, id)) {
         if (isCall)
             jsop_callelem_slow();
         else
             jsop_getelem_slow();
         return true;
     }
 
-    frame.forgetMismatchedObject(obj);
-
-    if (cx->typeInferenceEnabled()) {
+    if (cx->typeInferenceEnabled() && id->mightBeType(JSVAL_TYPE_INT32) && !isCall) {
         types::TypeSet *types = analysis->poppedTypes(PC, 1);
-        if (!isCall && id->mightBeType(JSVAL_TYPE_INT32) &&
+        if (types->isLazyArguments(cx) && !outerScript->analysis(cx)->modifiesArguments()) {
+            jsop_getelem_args();
+            return true;
+        }
+        if (obj->mightBeType(JSVAL_TYPE_OBJECT) &&
             !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) &&
             !arrayPrototypeHasIndexedProperty()) {
             // this is definitely a dense array, generate code directly without
             // using an inline cache.
             bool packed = !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY);
             jsop_getelem_dense(packed);
             return true;
         }
     }
 
+    frame.forgetMismatchedObject(obj);
+
     GetElementICInfo ic = GetElementICInfo(JSOp(*PC));
 
     // Pin the top of the stack to avoid spills, before allocating registers.
     MaybeRegisterID pinnedIdData = frame.maybePinData(id);
     MaybeRegisterID pinnedIdType = frame.maybePinType(id);
 
     MaybeJump objTypeGuard;
     if (!obj->isTypeKnown()) {
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -501,18 +501,18 @@ LoopState::setLoopReg(AnyRegisterID reg,
         alloc->set(reg, slot, true);
     }
 }
 
 bool
 LoopState::hoistArrayLengthCheck(const CrossSSAValue &obj, const CrossSSAValue &index)
 {
     /*
-     * Note: this method requires that obj is either a dense array or not an
-     * object, and that the index is definitely an integer.
+     * Note: this method requires that the index is definitely an integer, and
+     * that obj is either a dense array or not an object.
      */
     if (skipAnalysis)
         return false;
 
     uint32 objSlot;
     int32 objConstant;
     if (!getEntryValue(obj, &objSlot, &objConstant) || objConstant != 0)
         return false;
@@ -624,16 +624,68 @@ LoopState::hoistArrayLengthCheck(const C
         return addHoistedCheck(objSlot, indexSlot, testLHS, constant);
     }
 
     JaegerSpew(JSpew_Analysis, "No match found\n");
     return false;
 }
 
 bool
+LoopState::hoistArgsLengthCheck(const CrossSSAValue &index)
+{
+    if (skipAnalysis)
+        return false;
+
+    JaegerSpew(JSpew_Analysis, "Trying to hoist argument range check\n");
+
+    uint32 indexSlot;
+    int32 indexConstant;
+    if (!getEntryValue(index, &indexSlot, &indexConstant)) {
+        JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n");
+        return false;
+    }
+
+    /*
+     * We only hoist arguments checks which can be completely eliminated, for
+     * now just tests with 'i < arguments.length' or similar in the condition.
+     */
+
+    if (indexSlot == UNASSIGNED || loopInvariantEntry(indexSlot)) {
+        JaegerSpew(JSpew_Analysis, "Index is constant or loop invariant\n");
+        return false;
+    }
+
+    if (!outerAnalysis->liveness(indexSlot).nonDecreasing(outerScript, lifetime)) {
+        JaegerSpew(JSpew_Analysis, "Index may decrease in future iterations\n");
+        return false;
+    }
+
+    if (indexSlot == testLHS && indexConstant == 0 && testConstant == -1 && testLessEqual) {
+        bool found = false;
+        for (unsigned i = 0; i < invariantEntries.length(); i++) {
+            const InvariantEntry &entry = invariantEntries[i];
+            if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH) {
+                uint32 slot = frame.outerSlot(frame.getTemporary(entry.u.array.temporary));
+                if (slot == testRHS)
+                    found = true;
+                break;
+            }
+        }
+        if (found) {
+            addNegativeCheck(indexSlot, indexConstant);
+            JaegerSpew(JSpew_Analysis, "Access implied by loop test\n");
+            return true;
+        }
+    }
+
+    JaegerSpew(JSpew_Analysis, "No match found\n");
+    return false;
+}
+
+bool
 LoopState::hasTestLinearRelationship(uint32 slot)
 {
     /*
      * Determine whether slot has a linear relationship with the loop test
      * variable 'test', such that (slot + test) always has the same value at
      * the head of the loop.
      */
 
@@ -662,18 +714,20 @@ LoopState::hasTestLinearRelationship(uin
       case JSOP_ARGDEC:
         return true;
       default:
         return false;
     }
 }
 
 FrameEntry *
-LoopState::invariantSlots(const CrossSSAValue &obj)
+LoopState::invariantArraySlots(const CrossSSAValue &obj)
 {
+    JS_ASSERT(!skipAnalysis);
+
     uint32 objSlot;
     int32 objConstant;
     if (!getEntryValue(obj, &objSlot, &objConstant) || objConstant != 0) {
         JS_NOT_REACHED("Bad value");
         return NULL;
     }
 
     for (unsigned i = 0; i < invariantEntries.length(); i++) {
@@ -685,38 +739,90 @@ LoopState::invariantSlots(const CrossSSA
     }
 
     /* addHoistedCheck should have ensured there is an entry for the slots. */
     JS_NOT_REACHED("Missing invariant slots");
     return NULL;
 }
 
 FrameEntry *
+LoopState::invariantArguments()
+{
+    if (skipAnalysis)
+        return NULL;
+
+    for (unsigned i = 0; i < invariantEntries.length(); i++) {
+        InvariantEntry &entry = invariantEntries[i];
+        if (entry.kind == InvariantEntry::INVARIANT_ARGS_BASE)
+            return frame.getTemporary(entry.u.array.temporary);
+    }
+
+    uint32 which = frame.allocTemporary();
+    if (which == uint32(-1))
+        return NULL;
+    FrameEntry *fe = frame.getTemporary(which);
+
+    InvariantEntry entry;
+    entry.kind = InvariantEntry::INVARIANT_ARGS_BASE;
+    entry.u.array.temporary = which;
+    invariantEntries.append(entry);
+
+    JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args base\n",
+               frame.entryName(fe));
+    return fe;
+}
+
+FrameEntry *
 LoopState::invariantLength(const CrossSSAValue &obj)
 {
     if (skipAnalysis)
         return NULL;
 
     uint32 objSlot;
     int32 objConstant;
     if (!getEntryValue(obj, &objSlot, &objConstant) || objConstant != 0)
         return NULL;
+    TypeSet *objTypes = ssa->getValueTypes(obj);
+
+    /* Check for 'length' on the lazy arguments for the current frame. */
+    if (objTypes->isLazyArguments(cx)) {
+        JS_ASSERT(obj.frame == CrossScriptSSA::OUTER_FRAME);
+
+        for (unsigned i = 0; i < invariantEntries.length(); i++) {
+            InvariantEntry &entry = invariantEntries[i];
+            if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH)
+                return frame.getTemporary(entry.u.array.temporary);
+        }
+
+        uint32 which = frame.allocTemporary();
+        if (which == uint32(-1))
+            return NULL;
+        FrameEntry *fe = frame.getTemporary(which);
+
+        InvariantEntry entry;
+        entry.kind = InvariantEntry::INVARIANT_ARGS_LENGTH;
+        entry.u.array.temporary = which;
+        invariantEntries.append(entry);
+
+        JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args length\n",
+                   frame.entryName(fe));
+        return fe;
+    }
 
     for (unsigned i = 0; i < invariantEntries.length(); i++) {
         InvariantEntry &entry = invariantEntries[i];
         if (entry.kind == InvariantEntry::INVARIANT_LENGTH &&
             entry.u.array.arraySlot == objSlot) {
             return frame.getTemporary(entry.u.array.temporary);
         }
     }
 
     if (!loopInvariantEntry(objSlot))
         return NULL;
 
-    TypeSet *objTypes = ssa->getValueTypes(obj);
     if (objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY))
         return NULL;
 
     /*
      * Don't make 'length' loop invariant if the loop might directly write
      * to the elements of any of the accessed arrays. This could invoke an
      * inline path which updates the length. There is no need to check the
      * modset for direct 'length' writes, as we don't generate inline paths
@@ -1235,16 +1341,30 @@ LoopState::restoreInvariants(jsbytecode 
             masm.loadPtr(Address(T0, offset), T0);
             if (entry.kind == InvariantEntry::INVARIANT_LENGTH)
                 masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
             else
                 masm.storePtr(T0, address);
             break;
           }
 
+          case InvariantEntry::INVARIANT_ARGS_BASE: {
+            Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
+            masm.loadFrameActuals(outerScript->fun, T0);
+            masm.storePtr(T0, address);
+            break;
+          }
+
+          case InvariantEntry::INVARIANT_ARGS_LENGTH: {
+            Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
+            masm.load32(Address(JSFrameReg, StackFrame::offsetOfArgs()), T0);
+            masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
+            break;
+          }
+
           case InvariantEntry::INVARIANT_PROPERTY: {
             uint32 object = entry.u.property.objectSlot;
             Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(object));
             jumps->append(notObject);
             masm.loadPayload(frame.addressOf(object), T0);
 
             masm.loadInlineSlot(T0, entry.u.property.propertySlot, T1, T0);
             masm.storeValueFromComponents(T1, T0,
--- a/js/src/methodjit/LoopState.h
+++ b/js/src/methodjit/LoopState.h
@@ -167,19 +167,25 @@ class LoopState : public MacroAssemblerT
             BOUNDS_CHECK,
 
             /* value1 + constant >= 0 */
             NEGATIVE_CHECK,
 
             /* constant >= value1 + value2 */
             RANGE_CHECK,
 
+            /* For dense arrays */
             INVARIANT_SLOTS,
             INVARIANT_LENGTH,
 
+            /* For lazy arguments */
+            INVARIANT_ARGS_BASE,
+            INVARIANT_ARGS_LENGTH,
+
+            /* For definite properties */
             INVARIANT_PROPERTY
         } kind;
         union {
             struct {
                 uint32 arraySlot;
                 uint32 valueSlot1;
                 uint32 valueSlot2;
                 int32 constant;
@@ -271,17 +277,22 @@ class LoopState : public MacroAssemblerT
     void flushLoop(StubCompiler &stubcc);
 
     /*
      * These should only be used for entries which are known to be dense arrays
      * (if they are objects at all).
      */
     bool hoistArrayLengthCheck(const analyze::CrossSSAValue &obj,
                                const analyze::CrossSSAValue &index);
-    FrameEntry *invariantSlots(const analyze::CrossSSAValue &obj);
+    FrameEntry *invariantArraySlots(const analyze::CrossSSAValue &obj);
+
+    /* Methods for accesses on lazy arguments. */
+    bool hoistArgsLengthCheck(const analyze::CrossSSAValue &index);
+    FrameEntry *invariantArguments();
+
     FrameEntry *invariantLength(const analyze::CrossSSAValue &obj);
     FrameEntry *invariantProperty(const analyze::CrossSSAValue &obj, jsid id);
 
     /* Whether a binary or inc/dec op's result cannot overflow. */
     bool cannotIntegerOverflow(const analyze::CrossSSAValue &pushed);
 
     /*
      * Whether integer overflow in addition or negative zeros in multiplication
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -869,16 +869,18 @@ mjit::EnterMethodJIT(JSContext *cx, Stac
                script->filename, script->lineno);
     prof.start();
 #endif
 
     JS_ASSERT(cx->fp() == fp);
     FrameRegs &oldRegs = cx->regs();
 
     fp->scopeChain();
+    if (fp->isFunctionFrame() && fp->script()->usesArguments)
+        fp->ensureCoherentArgCount();
 
     JSBool ok;
     {
         AssertCompartmentUnchanged pcc(cx);
         JSAutoResolveFlags rf(cx, RESOLVE_INFER);
         ok = JaegerTrampoline(cx, fp, code, stackLimit);
     }
 
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -1718,16 +1718,20 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic
             GetPropCompiler cc(f, script, NULL, *pic, NULL, DisabledGetPropIC);
             LookupStatus status = cc.generateStringLengthStub();
             if (status == Lookup_Error)
                 THROW();
             JSString *str = f.regs.sp[-1].toString();
             f.regs.sp[-1].setInt32(str->length());
             f.script()->typeMonitor(f.cx, f.pc(), f.regs.sp[-1]);
             return;
+        } else if (f.regs.sp[-1].isMagic(JS_LAZY_ARGUMENTS)) {
+            f.regs.sp[-1].setInt32(f.regs.fp()->numActualArgs());
+            f.script()->typeMonitor(f.cx, f.pc(), f.regs.sp[-1]);
+            return;
         } else if (!f.regs.sp[-1].isPrimitive()) {
             JSObject *obj = &f.regs.sp[-1].toObject();
             if (obj->isArray() ||
                 (obj->isArguments() && !obj->asArguments()->hasOverriddenLength()) ||
                 obj->isString()) {
                 GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledGetPropIC);
                 if (obj->isArray()) {
                     LookupStatus status = cc.generateArrayLengthStub();
@@ -2514,17 +2518,17 @@ ic::CallElement(VMFrame &f, ic::GetEleme
     f.script()->typeMonitor(cx, f.pc(), f.regs.sp[-2]);
 }
 
 void JS_FASTCALL
 ic::GetElement(VMFrame &f, ic::GetElementIC *ic)
 {
     JSContext *cx = f.cx;
 
-    // Right now, we don't optimize for strings.
+    // Right now, we don't optimize for strings or lazy arguments.
     if (!f.regs.sp[-2].isObject()) {
         ic->disable(cx, "non-object");
         stubs::GetElem(f);
         return;
     }
 
     Value idval = f.regs.sp[-1];
 
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -227,40 +227,36 @@ Recompiler::expandInlineFrames(JSContext
     JS_ASSERT_IF(next, next->prev() == fp && next->prevInline() == inlined);
 
     /*
      * Treat any frame expansion as a recompilation event, so that f.jit() is
      * stable if no recompilations have occurred.
      */
     cx->compartment->types.frameExpansions++;
 
-    RejoinState rejoin = (RejoinState) f->stubRejoin;
-    JS_ASSERT(rejoin != REJOIN_NATIVE && rejoin != REJOIN_NATIVE_LOWERED);
-
     /*
      * Patch the VMFrame's return address if it is returning at the given inline site.
      * Note there is no worry about handling a native or CompileFunction call here,
      * as such IC stubs are not generated within inline frames.
      */
     void **frameAddr = f->returnAddressLocation();
     uint8* codeStart = (uint8 *)fp->jit()->code.m_code.executableAddress();
 
     InlineFrame *inner = &fp->jit()->inlineFrames()[inlined->inlineIndex];
     jsbytecode *innerpc = inner->fun->script()->code + inlined->pcOffset;
 
     StackFrame *innerfp = expandInlineFrameChain(cx, fp, inner);
 
     /* Check if the VMFrame returns into the inlined frame. */
-    if (f->stubRejoin) {
+    if (f->stubRejoin && (f->stubRejoin & 0x1) && f->regs.fp()->prev() == fp) {
         /* The VMFrame is calling CompileFunction. */
-        if (f->regs.fp()->prev() == fp) {
-            fp->prev()->setRejoin(StubRejoin(rejoin));
-            *frameAddr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
-        }
-    } else if (*frameAddr == codeStart + inlined->codeOffset) {
+        fp->prev()->setRejoin(StubRejoin((RejoinState) f->stubRejoin));
+        *frameAddr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
+    }
+    if (*frameAddr == codeStart + inlined->codeOffset) {
         /* The VMFrame returns directly into the expanded frame. */
         SetRejoinState(innerfp, *inlined, frameAddr);
     }
 
     if (f->fp() == fp) {
         JS_ASSERT(f->regs.inlined() == inlined);
         f->regs.expandInline(innerfp, innerpc);
     }
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -438,16 +438,27 @@ stubs::GetElem(VMFrame &f)
             if (!str)
                 THROW();
             f.regs.sp[-2].setString(str);
             f.script()->typeMonitor(cx, f.pc(), f.regs.sp[-2]);
             return;
         }
     }
 
+    if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
+        if (rref.isInt32() && size_t(rref.toInt32()) < regs.fp()->numActualArgs()) {
+            regs.sp[-2] = regs.fp()->canonicalActualArg(rref.toInt32());
+            f.script()->typeMonitor(cx, f.pc(), regs.sp[-2]);
+            return;
+        }
+        cx->markTypeObjectFlags(f.script()->fun->getType(),
+                                types::OBJECT_FLAG_CREATED_ARGUMENTS);
+        JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
+    }
+
     JSObject *obj = ValueToObject(cx, &lref);
     if (!obj)
         THROW();
 
     const Value *copyFrom;
     Value rval;
     jsid id;
     if (rref.isInt32()) {
@@ -1960,16 +1971,24 @@ template void JS_FASTCALL stubs::DecGlob
 
 static bool JS_FASTCALL
 InlineGetProp(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
 
     Value *vp = &f.regs.sp[-1];
+
+    if (vp->isMagic(JS_LAZY_ARGUMENTS)) {
+        JS_ASSERT(js_GetOpcode(cx, f.script(), f.pc()) == JSOP_LENGTH);
+        regs.sp[-1] = Int32Value(regs.fp()->numActualArgs());
+        f.script()->typeMonitor(cx, f.pc(), regs.sp[-1]);
+        return true;
+    }
+
     JSObject *obj = ValueToObject(f.cx, vp);
     if (!obj)
         return false;
 
     Value rval;
     do {
         /*
          * We do not impose the method read barrier if in an imacro,
@@ -2821,24 +2840,24 @@ stubs::CheckArgumentTypes(VMFrame &f)
 void JS_FASTCALL
 stubs::AssertArgumentTypes(VMFrame &f)
 {
     StackFrame *fp = f.fp();
     JSFunction *fun = fp->fun();
     JSScript *script = fun->script();
 
     types::jstype type = types::GetValueType(f.cx, fp->thisValue());
-    if (!script->thisTypes()->hasType(type)) {
+    if (!types::TypeMatches(f.cx, script->thisTypes(), type)) {
         types::TypeFailure(f.cx, "Missing type for #%u this: %s", script->id(),
                            types::TypeString(type));
     }
 
     for (unsigned i = 0; i < fun->nargs; i++) {
         type = types::GetValueType(f.cx, fp->formalArg(i));
-        if (!script->argTypes(i)->hasType(type)) {
+        if (!types::TypeMatches(f.cx, script->argTypes(i), type)) {
             types::TypeFailure(f.cx, "Missing type for #%u arg %d: %s", script->id(), i,
                                types::TypeString(type));
         }
     }
 }
 #endif
 
 /*
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -491,20 +491,16 @@ StackFrame::stealFrameAndSlots(Value *vp
     JS_ASSERT(vp == (Value *)this - (otherfp->formalArgsEnd() - othervp));
     JS_ASSERT(othervp == otherfp->actualArgs() - 2);
     JS_ASSERT(othersp >= otherfp->slots());
     JS_ASSERT(othersp <= otherfp->base() + otherfp->numSlots());
 
     PodCopy(vp, othervp, othersp - othervp);
     JS_ASSERT(vp == this->actualArgs() - 2);
 
-    /* Catch bad-touching of non-canonical args (e.g., generator_trace). */
-    if (otherfp->hasOverflowArgs())
-        Debug_SetValueRangeToCrashOnTouch(othervp, othervp + 2 + otherfp->numFormalArgs());
-
     /*
      * Repoint Call, Arguments, Block and With objects to the new live frame.
      * Call and Arguments are done directly because we have pointers to them.
      * Block and With objects are done indirectly through 'liveFrame'. See
      * js_LiveFrameToFloating comment in jsiter.h.
      */
     if (hasCallObj()) {
         JSObject &obj = callObj();
@@ -601,16 +597,23 @@ inline uintN
 StackFrame::numActualArgs() const
 {
     JS_ASSERT(hasArgs());
     if (JS_UNLIKELY(flags_ & (OVERFLOW_ARGS | UNDERFLOW_ARGS)))
         return hasArgsObj() ? argsObj().initialLength() : args.nactual;
     return numFormalArgs();
 }
 
+inline void
+StackFrame::ensureCoherentArgCount()
+{
+    if (!hasArgsObj())
+        args.nactual = numActualArgs();
+}
+
 inline Value *
 StackFrame::actualArgs() const
 {
     JS_ASSERT(hasArgs());
     Value *argv = formalArgs();
     if (JS_UNLIKELY(flags_ & OVERFLOW_ARGS)) {
         uintN nactual = hasArgsObj() ? argsObj().initialLength() : args.nactual;
         return argv - (2 + nactual);
@@ -898,17 +901,16 @@ ContextStack::getCallFrame(JSContext *cx
     *flags |= StackFrame::OVERFLOW_ARGS;
     uintN ncopy = 2 + nformal;
     if (JS_UNLIKELY(!check(cx, space(), firstUnused, ncopy + nvals)))
         return NULL;
 
     Value *dst = firstUnused;
     Value *src = firstUnused - (2 + nactual);
     PodCopy(dst, src, ncopy);
-    Debug_SetValueRangeToCrashOnTouch(src, ncopy);
     return reinterpret_cast<StackFrame *>(firstUnused + ncopy);
 }
 
 JS_ALWAYS_INLINE StackFrame *
 ContextStack::getInlineFrame(JSContext *cx, Value *sp, uintN nactual,
                              JSFunction *fun, JSScript *script, uint32 *flags) const
 {
     JS_ASSERT(isCurrentAndActive());
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -572,16 +572,17 @@ class StackFrame
         return (flags_ & (FUNCTION | EVAL)) == FUNCTION
                ? formalArgs()
                : NULL;
     }
 
     inline uintN numActualArgs() const;
     inline js::Value *actualArgs() const;
     inline js::Value *actualArgsEnd() const;
+    inline void ensureCoherentArgCount();
 
     inline js::Value &canonicalActualArg(uintN i) const;
     template <class Op> inline bool forEachCanonicalActualArg(Op op);
     template <class Op> inline bool forEachFormalArg(Op op);
 
     inline void clearMissingArgs();
 
     bool hasArgsObj() const {
@@ -953,16 +954,20 @@ class StackFrame
     static size_t offsetOfExec() {
         return offsetof(StackFrame, exec);
     }
 
     void *addressOfArgs() {
         return &args;
     }
 
+    static size_t offsetOfArgs() {
+        return offsetof(StackFrame, args);
+    }
+
     static size_t offsetOfScopeChain() {
         return offsetof(StackFrame, scopeChain_);
     }
 
     JSObject **addressOfScopeChain() {
         JS_ASSERT(flags_ & HAS_SCOPECHAIN);
         return &scopeChain_;
     }