Bug 918405 - Enable OSR-ing into Ion in functions with needsArgsObj. r=h4writer
authorKannan Vijayan <kvijayan@mozilla.com>
Mon, 23 Sep 2013 10:50:30 -0400
changeset 148329 cd646a300ffee771ea4a94e3e4910e1901a043da
parent 148328 7a733fffc6e92351b64b1775e91b41edd9f7bade
child 148330 a8ba4fdc133fdc8c09a4c7f5bc3a0f6cb7387f4b
push id25336
push userryanvm@gmail.com
push dateMon, 23 Sep 2013 21:01:41 +0000
treeherdermozilla-central@8b28b4bed72c [default view] [failures only]
reviewersh4writer
bugs918405
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 918405 - Enable OSR-ing into Ion in functions with needsArgsObj. r=h4writer
js/src/jit/BaselineFrame.h
js/src/jit/BaselineIC.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/Ion.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MIRGraph.cpp
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/vm/Stack.h
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -153,18 +153,18 @@ class BaselineFrame
     Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
         JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i));
         JS_ASSERT(i < script()->nfixed);
         return *valueSlot(i);
     }
 
     Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
         JS_ASSERT(i < numFormalArgs());
-        JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
-        JS_ASSERT_IF(checkAliasing, !script()->formalIsAliased(i));
+        JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() &&
+                                    !script()->formalIsAliased(i));
         return argv()[i];
     }
 
     Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
         JS_ASSERT(i < numActualArgs());
         JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
         JS_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i));
         return argv()[i];
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -842,19 +842,23 @@ PrepareOsrTempData(JSContext *cx, ICUseC
 
     //
     // Initialize the fake StackFrame.
     //
 
     // Copy formal args and thisv.
     memcpy(stackFrameStart, frame->argv() - 1, (numFormalArgs + 1) * sizeof(Value));
 
-    // Initialize ScopeChain, Exec, and Flags fields in StackFrame struct.
+    // Initialize ScopeChain, Exec, ArgsObj, and Flags fields in StackFrame struct.
     uint8_t *stackFrame = info->stackFrame;
     *((JSObject **) (stackFrame + StackFrame::offsetOfScopeChain())) = frame->scopeChain();
+    if (frame->script()->needsArgsObj()) {
+        JS_ASSERT(frame->hasArgsObj());
+        *((JSObject **) (stackFrame + StackFrame::offsetOfArgumentsObject())) = &frame->argsObj();
+    }
     if (frame->isFunctionFrame()) {
         // Store the function in exec field, and StackFrame::FUNCTION for flags.
         *((JSFunction **) (stackFrame + StackFrame::offsetOfExec())) = frame->fun();
         *((uint32_t *) (stackFrame + StackFrame::offsetOfFlags())) = StackFrame::FUNCTION;
     } else {
         *((JSScript **) (stackFrame + StackFrame::offsetOfExec())) = frame->script();
         *((uint32_t *) (stackFrame + StackFrame::offsetOfFlags())) = 0;
     }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1020,16 +1020,28 @@ CodeGenerator::visitOsrScopeChain(LOsrSc
 
     const ptrdiff_t frameOffset = StackFrame::offsetOfScopeChain();
 
     masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
     return true;
 }
 
 bool
+CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject *lir)
+{
+    const LAllocation *frame   = lir->getOperand(0);
+    const LDefinition *object  = lir->getDef(0);
+
+    const ptrdiff_t frameOffset = StackFrame::offsetOfArgumentsObject();
+
+    masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
+    return true;
+}
+
+bool
 CodeGenerator::visitStackArgT(LStackArgT *lir)
 {
     const LAllocation *arg = lir->getArgument();
     MIRType argType = lir->mir()->getArgument()->type();
     uint32_t argslot = lir->argslot();
 
     int32_t stack_offset = StackOffsetOfPassedArg(argslot);
     Address dest(StackPointer, stack_offset);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -61,16 +61,17 @@ class CodeGenerator : public CodeGenerat
     bool visitParameter(LParameter *lir);
     bool visitCallee(LCallee *lir);
     bool visitStart(LStart *lir);
     bool visitReturn(LReturn *ret);
     bool visitDefVar(LDefVar *lir);
     bool visitDefFun(LDefFun *lir);
     bool visitOsrEntry(LOsrEntry *lir);
     bool visitOsrScopeChain(LOsrScopeChain *lir);
+    bool visitOsrArgumentsObject(LOsrArgumentsObject *lir);
     bool visitStackArgT(LStackArgT *lir);
     bool visitStackArgV(LStackArgV *lir);
     bool visitMoveGroup(LMoveGroup *group);
     bool visitValueToInt32(LValueToInt32 *lir);
     bool visitValueToDouble(LValueToDouble *lir);
     bool visitValueToFloat32(LValueToFloat32 *lir);
     bool visitFloat32ToDouble(LFloat32ToDouble *lir);
     bool visitDoubleToFloat32(LDoubleToFloat32 *lir);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1698,22 +1698,16 @@ CheckScript(JSContext *cx, JSScript *scr
         return false;
     }
 
     if (!script->analyzedArgsUsage() && !script->ensureRanAnalysis(cx)) {
         IonSpew(IonSpew_Abort, "OOM under ensureRanAnalysis");
         return false;
     }
 
-    if (osr && script->needsArgsObj()) {
-        // OSR-ing into functions with arguments objects is not supported.
-        IonSpew(IonSpew_Abort, "OSR script has argsobj");
-        return false;
-    }
-
     if (!script->compileAndGo) {
         IonSpew(IonSpew_Abort, "not compile-and-go");
         return false;
     }
 
     return true;
 }
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -18,16 +18,18 @@
 #include "jit/BaselineInspector.h"
 #include "jit/ExecutionModeInlines.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/IonSpewer.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
 
+#include "vm/ArgumentsObject.h"
+
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/CompileInfo-inl.h"
 
 using namespace js;
 using namespace js::jit;
@@ -1028,17 +1030,17 @@ IonBuilder::maybeAddOsrTypeBarriers()
     for (uint32_t i = info().startArgSlot(); i < osrBlock->stackDepth(); i++, headerPhi++) {
 
         // Aliased slots are never accessed, since they need to go through
         // the callobject. The typebarriers are added there and can be
         // discared here.
         if (info().isSlotAliased(i))
             continue;
 
-        MInstruction *def = osrBlock->getSlot(i)->toOsrValue();
+        MInstruction *def = osrBlock->getSlot(i)->toInstruction();
 
         JS_ASSERT(headerPhi->slot() == i);
         MPhi *preheaderPhi = preheader->getSlot(i)->toPhi();
 
         MIRType type = headerPhi->type();
         types::TemporaryTypeSet *typeSet = headerPhi->resultTypeSet();
 
         if (!addOsrValueTypeBarrier(i, &def, type, typeSet))
@@ -5749,44 +5751,63 @@ IonBuilder::newOsrPreheader(MBasicBlock 
             // slot.
             scopev = MConstant::New(UndefinedValue());
         }
 
         osrBlock->add(scopev);
         osrBlock->initSlot(slot, scopev);
     }
 
-    // Initialize arguments object.  Ion will not allow OSR-ing into scripts
-    // with |needsArgsObj| set, so this can be undefined.
-    JS_ASSERT(!info().needsArgsObj());
+    // Initialize arguments object.
+    bool needsArgsObj = info().needsArgsObj();
+    MInstruction *argsObj = NULL;
     if (info().hasArguments()) {
-        MInstruction *argsObj = MConstant::New(UndefinedValue());
+        if (needsArgsObj)
+            argsObj = MOsrArgumentsObject::New(entry);
+        else
+            argsObj = MConstant::New(UndefinedValue());
         osrBlock->add(argsObj);
         osrBlock->initSlot(info().argsObjSlot(), argsObj);
     }
 
     if (info().fun()) {
         // Initialize |this| parameter.
         uint32_t slot = info().thisSlot();
         ptrdiff_t offset = StackFrame::offsetOfThis(info().fun());
 
         MOsrValue *thisv = MOsrValue::New(entry, offset);
         osrBlock->add(thisv);
         osrBlock->initSlot(slot, thisv);
 
         // Initialize arguments.
         for (uint32_t i = 0; i < info().nargs(); i++) {
-            // NB: Ion does not OSR into any function which |needsArgsObj|, so
-            // using argSlot() here instead of argSlotUnchecked() is ok.
-            uint32_t slot = info().argSlot(i);
-            ptrdiff_t offset = StackFrame::offsetOfFormalArg(info().fun(), i);
-
-            MOsrValue *osrv = MOsrValue::New(entry, offset);
-            osrBlock->add(osrv);
-            osrBlock->initSlot(slot, osrv);
+            uint32_t slot = needsArgsObj ? info().argSlotUnchecked(i) : info().argSlot(i);
+
+            if (needsArgsObj) {
+                JS_ASSERT(argsObj && argsObj->isOsrArgumentsObject());
+                // If this is an aliased formal, then the arguments object
+                // contains a hole at this index.  Any references to this
+                // variable in the jitcode will come from JSOP_*ALIASEDVAR
+                // opcodes, so the slot itself can be set to undefined.  If
+                // it's not aliased, it must be retrieved from the arguments
+                // object.
+                MInstruction *osrv;
+                if (script()->formalIsAliased(i))
+                    osrv = MConstant::New(UndefinedValue());
+                else
+                    osrv = MGetArgumentsObjectArg::New(argsObj, i);
+
+                osrBlock->add(osrv);
+                osrBlock->initSlot(slot, osrv);
+            } else {
+                ptrdiff_t offset = StackFrame::offsetOfFormalArg(info().fun(), i);
+                MOsrValue *osrv = MOsrValue::New(entry, offset);
+                osrBlock->add(osrv);
+                osrBlock->initSlot(slot, osrv);
+            }
         }
     }
 
     // Initialize locals.
     for (uint32_t i = 0; i < info().nlocals(); i++) {
         uint32_t slot = info().localSlot(i);
         ptrdiff_t offset = StackFrame::offsetOfFixed(i);
 
@@ -5829,17 +5850,17 @@ IonBuilder::newOsrPreheader(MBasicBlock 
 
     // Treat the OSR values as having the same type as the existing values
     // coming in to the loop. These will be fixed up with appropriate
     // unboxing and type barriers in finishLoop, once the possible types
     // at the loop header are known.
     for (uint32_t i = info().startArgSlot(); i < osrBlock->stackDepth(); i++) {
         MDefinition *existing = current->getSlot(i);
         MDefinition *def = osrBlock->getSlot(i);
-        JS_ASSERT(def->type() == MIRType_Value);
+        JS_ASSERT_IF(!needsArgsObj || !info().isSlotAliased(i), def->type() == MIRType_Value);
 
         // Aliased slots are never accessed, since they need to go through
         // the callobject. No need to type them here.
         if (info().isSlotAliased(i))
             continue;
 
         def->setResultType(existing->type());
         def->setResultTypeSet(existing->resultTypeSet());
@@ -5871,38 +5892,42 @@ IonBuilder::newPendingLoopHeader(MBasicB
         // header. The OSR frame may have unexpected types due to type changes
         // within the loop body or due to incomplete profiling information,
         // in which case this may avoid restarts of loop analysis or bailouts
         // during the OSR itself.
 
         // Unbox the MOsrValue if it is known to be unboxable.
         for (uint32_t i = info().startArgSlot(); i < block->stackDepth(); i++) {
 
-            // The value of aliased slots are in the callobject. So we can't
+            // The value of aliased args and slots are in the callobject. So we can't
             // the value from the baseline frame.
             if (info().isSlotAliased(i))
                 continue;
 
             // Don't bother with expression stack values. The stack should be
             // empty except for let variables (not Ion-compiled) or iterators.
             if (i >= info().firstStackSlot())
                 continue;
 
             MPhi *phi = block->getSlot(i)->toPhi();
 
             // Get the value from the baseline frame.
             Value existingValue;
             uint32_t arg = i - info().firstArgSlot();
             uint32_t var = i - info().firstLocalSlot();
-            if (info().fun() && i == info().thisSlot())
+            if (info().fun() && i == info().thisSlot()) {
                 existingValue = baselineFrame_->thisValue();
-            else if (arg < info().nargs())
-                existingValue = baselineFrame_->unaliasedFormal(arg);
-            else
+            } else if (arg < info().nargs()) {
+                if (info().needsArgsObj())
+                    existingValue = baselineFrame_->argsObj().arg(arg);
+                else
+                    existingValue = baselineFrame_->unaliasedFormal(arg);
+            } else {
                 existingValue = baselineFrame_->unaliasedVar(var);
+            }
 
             // Extract typeset from value.
             MIRType type = existingValue.isDouble()
                          ? MIRType_Double
                          : MIRTypeFromValueType(existingValue.extractNonDoubleType());
             types::Type ntype = types::GetValueType(existingValue);
             types::TemporaryTypeSet *typeSet =
                 GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -2796,16 +2796,32 @@ class LOsrScopeChain : public LInstructi
         setOperand(0, entry);
     }
 
     const MOsrScopeChain *mir() {
         return mir_->toOsrScopeChain();
     }
 };
 
+// Materialize a JSObject ArgumentsObject stored in an interpreter frame for OSR.
+class LOsrArgumentsObject : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(OsrArgumentsObject)
+
+    LOsrArgumentsObject(const LAllocation &entry)
+    {
+        setOperand(0, entry);
+    }
+
+    const MOsrArgumentsObject *mir() {
+        return mir_->toOsrArgumentsObject();
+    }
+};
+
 class LRegExp : public LCallInstructionHelper<1, 0, 0>
 {
   public:
     LIR_HEADER(RegExp)
 
     const MRegExp *mir() const {
         return mir_->toRegExp();
     }
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -129,16 +129,17 @@
     _(DoubleToInt32)                \
     _(TruncateDToInt32)             \
     _(IntToString)                  \
     _(DoubleToString)               \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
+    _(OsrArgumentsObject)           \
     _(RegExp)                       \
     _(RegExpTest)                   \
     _(Lambda)                       \
     _(LambdaForSingleton)           \
     _(LambdaPar)                    \
     _(ImplicitThis)                 \
     _(Slots)                        \
     _(Elements)                     \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1542,16 +1542,23 @@ LIRGenerator::visitOsrValue(MOsrValue *v
 bool
 LIRGenerator::visitOsrScopeChain(MOsrScopeChain *object)
 {
     LOsrScopeChain *lir = new LOsrScopeChain(useRegister(object->entry()));
     return define(lir, object);
 }
 
 bool
+LIRGenerator::visitOsrArgumentsObject(MOsrArgumentsObject *object)
+{
+    LOsrArgumentsObject *lir = new LOsrArgumentsObject(useRegister(object->entry()));
+    return define(lir, object);
+}
+
+bool
 LIRGenerator::visitToDouble(MToDouble *convert)
 {
     MDefinition *opd = convert->input();
     mozilla::DebugOnly<MToDouble::ConversionKind> conversion = convert->conversion();
 
     switch (opd->type()) {
       case MIRType_Value:
       {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -150,16 +150,17 @@ class LIRGenerator : public LIRGenerator
     bool visitConcatPar(MConcatPar *ins);
     bool visitCharCodeAt(MCharCodeAt *ins);
     bool visitFromCharCode(MFromCharCode *ins);
     bool visitStart(MStart *start);
     bool visitOsrEntry(MOsrEntry *entry);
     bool visitNop(MNop *nop);
     bool visitOsrValue(MOsrValue *value);
     bool visitOsrScopeChain(MOsrScopeChain *object);
+    bool visitOsrArgumentsObject(MOsrArgumentsObject *object);
     bool visitToDouble(MToDouble *convert);
     bool visitToFloat32(MToFloat32 *convert);
     bool visitToInt32(MToInt32 *convert);
     bool visitTruncateToInt32(MTruncateToInt32 *truncate);
     bool visitToString(MToString *convert);
     bool visitRegExp(MRegExp *ins);
     bool visitRegExpTest(MRegExpTest *ins);
     bool visitLambda(MLambda *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4281,16 +4281,38 @@ class MOsrScopeChain : public MUnaryInst
         return new MOsrScopeChain(entry);
     }
 
     MOsrEntry *entry() {
         return getOperand(0)->toOsrEntry();
     }
 };
 
+// MIR representation of a JSObject ArgumentsObject pointer on the OSR StackFrame.
+// The pointer is indexed off of OsrFrameReg.
+class MOsrArgumentsObject : public MUnaryInstruction
+{
+  private:
+    MOsrArgumentsObject(MOsrEntry *entry)
+      : MUnaryInstruction(entry)
+    {
+        setResultType(MIRType_Object);
+    }
+
+  public:
+    INSTRUCTION_HEADER(OsrArgumentsObject)
+    static MOsrArgumentsObject *New(MOsrEntry *entry) {
+        return new MOsrArgumentsObject(entry);
+    }
+
+    MOsrEntry *entry() {
+        return getOperand(0)->toOsrEntry();
+    }
+};
+
 // Check the current frame for over-recursion past the global stack limit.
 class MCheckOverRecursed : public MNullaryInstruction
 {
   public:
     INSTRUCTION_HEADER(CheckOverRecursed)
 };
 
 // Check the current frame for over-recursion past the global stack limit.
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -451,19 +451,30 @@ MBasicBlock::linkOsrValues(MStart *start
     MResumePoint *res = start->resumePoint();
 
     for (uint32_t i = 0; i < stackDepth(); i++) {
         MDefinition *def = slots_[i];
         if (i == info().scopeChainSlot()) {
             if (def->isOsrScopeChain())
                 def->toOsrScopeChain()->setResumePoint(res);
         } else if (info().hasArguments() && i == info().argsObjSlot()) {
-            JS_ASSERT(def->isConstant() && def->toConstant()->value() == UndefinedValue());
+            JS_ASSERT(def->isConstant() || def->isOsrArgumentsObject());
+            JS_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue());
+            if (def->isOsrArgumentsObject())
+                def->toOsrArgumentsObject()->setResumePoint(res);
         } else {
-            def->toOsrValue()->setResumePoint(res);
+            JS_ASSERT(def->isOsrValue() || def->isGetArgumentsObjectArg() || def->isConstant());
+            // A constant Undefined can show up here for an argument slot when the function uses
+            // a heavyweight argsobj, but the argument in question is stored on the scope chain.
+            JS_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue());
+
+            if (def->isOsrValue())
+                def->toOsrValue()->setResumePoint(res);
+            else if (def->isGetArgumentsObjectArg())
+                def->toGetArgumentsObjectArg()->setResumePoint(res);
         }
     }
 }
 
 void
 MBasicBlock::setSlot(uint32_t slot, MDefinition *ins)
 {
     slots_[slot] = ins;
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -19,16 +19,17 @@ namespace jit {
     _(Test)                                                                 \
     _(TypeObjectDispatch)                                                   \
     _(FunctionDispatch)                                                     \
     _(Compare)                                                              \
     _(Phi)                                                                  \
     _(Beta)                                                                 \
     _(OsrValue)                                                             \
     _(OsrScopeChain)                                                        \
+    _(OsrArgumentsObject)                                                   \
     _(ReturnFromCtor)                                                       \
     _(CheckOverRecursed)                                                    \
     _(DefVar)                                                               \
     _(DefFun)                                                               \
     _(CreateThis)                                                           \
     _(CreateThisWithProto)                                                  \
     _(CreateThisWithTemplate)                                               \
     _(CreateArgumentsObject)                                                \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -111,16 +111,17 @@ class ParallelSafetyVisitor : public MIn
     SAFE_OP(TableSwitch)
     SAFE_OP(Goto)
     SAFE_OP(Test)
     SAFE_OP(Compare)
     SAFE_OP(Phi)
     SAFE_OP(Beta)
     UNSAFE_OP(OsrValue)
     UNSAFE_OP(OsrScopeChain)
+    UNSAFE_OP(OsrArgumentsObject)
     UNSAFE_OP(ReturnFromCtor)
     CUSTOM_OP(CheckOverRecursed)
     UNSAFE_OP(DefVar)
     UNSAFE_OP(DefFun)
     UNSAFE_OP(CreateThis)
     UNSAFE_OP(CreateThisWithTemplate)
     UNSAFE_OP(CreateThisWithProto)
     UNSAFE_OP(CreateArgumentsObject)
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -939,16 +939,20 @@ class StackFrame
     static size_t offsetOfNumActual() {
         return offsetof(StackFrame, u.nactual);
     }
 
     static size_t offsetOfScopeChain() {
         return offsetof(StackFrame, scopeChain_);
     }
 
+    static size_t offsetOfArgumentsObject() {
+        return offsetof(StackFrame, argsObj_);
+    }
+
     static ptrdiff_t offsetOfThis(JSFunction *fun) {
         return fun == NULL
                ? -1 * ptrdiff_t(sizeof(Value))
                : -(fun->nargs + 1) * ptrdiff_t(sizeof(Value));
     }
 
     static ptrdiff_t offsetOfFormalArg(JSFunction *fun, unsigned i) {
         JS_ASSERT(i < fun->nargs);