Ignore:
Timestamp:
Dec 11, 2011, 4:35:51 PM (13 years ago)
Author:
[email protected]
Message:

v8 benchmark takes 12-13 million function call slow paths due to extra arguments
https://bugs.webkit.org/show_bug.cgi?id=74244

Reviewed by Filip Pizlo.

.arguments function of order the Reversed

10% speedup on v8-raytrace, 1.7% speedup on v8 overall, neutral on Kraken
and SunSpider.

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::valueProfileForArgument): Clarified that the interface
to this function is an argument number.

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::emitCall):
(JSC::BytecodeGenerator::emitConstruct):
(JSC::BytecodeGenerator::isArgumentNumber): Switched to using CallFrame
helper functions for computing offsets for arguments, rather than doing
the math by hand.

Switched to iterating argument offsets backwards (--) instead of forwards (++).

  • bytecompiler/BytecodeGenerator.h:

(JSC::CallArguments::thisRegister):
(JSC::CallArguments::argumentRegister):
(JSC::CallArguments::registerOffset): Updated for arguments being reversed.

  • bytecompiler/NodesCodegen.cpp: Allocate arguments in reverse order.
  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::getArgument):
(JSC::DFG::ByteCodeParser::setArgument):
(JSC::DFG::ByteCodeParser::flush):
(JSC::DFG::ByteCodeParser::addCall):
(JSC::DFG::ByteCodeParser::handleCall):
(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::handleMinMax):
(JSC::DFG::ByteCodeParser::handleIntrinsic):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::processPhiStack): Use abstract argument indices
that just-in-time convert to bytecode operands (i.e., indexes in the register
file) through helper functions. This means only one piece of code needs
to know how arguments are laid out in the register file.

  • dfg/DFGGraph.cpp:

(JSC::DFG::Graph::dump): Ditto.

  • dfg/DFGGraph.h:

(JSC::DFG::Graph::valueProfileFor): Ditto.

  • dfg/DFGJITCompiler.cpp:

(JSC::DFG::JITCompiler::compileFunction): The whole point of this patch:
Treat too many arguments as an arity match.

  • dfg/DFGOSRExit.h:

(JSC::DFG::OSRExit::variableForIndex):
(JSC::DFG::OSRExit::operandForIndex): Use helper functions, as above.

  • dfg/DFGOperands.h:

(JSC::DFG::operandToArgument):
(JSC::DFG::argumentToOperand): These are now the only two lines of code in
the DFG compiler that know how arguments are laid out in memory.

(JSC::DFG::Operands::operand):
(JSC::DFG::Operands::setOperand): Use helper functions, as above.

  • dfg/DFGOperations.cpp: The whole point of this patch:

Treat too many arguments as an arity match.

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::emitCall): Use helper functions, as above.

Also, don't tag the caller frame slot as a cell, because it's not a cell.

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::emitCall): Use helper functions, as above.

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compile): Use helper functions, as above.

(JSC::DFG::SpeculativeJIT::checkArgumentTypes): Use already-computed
argument virtual register instead of recomputing by hand.

  • dfg/DFGSpeculativeJIT.h:

(JSC::DFG::SpeculativeJIT::callFrameSlot):
(JSC::DFG::SpeculativeJIT::argumentSlot):
(JSC::DFG::SpeculativeJIT::callFrameTagSlot):
(JSC::DFG::SpeculativeJIT::callFramePayloadSlot):
(JSC::DFG::SpeculativeJIT::argumentTagSlot):
(JSC::DFG::SpeculativeJIT::argumentPayloadSlot): Added a few helper
functions for dealing with callee arguments specifically. These still
build on top of our other helper functions, and have no direct knowledge
of how arguments are laid out in the register file.

(JSC::DFG::SpeculativeJIT::resetCallArguments):
(JSC::DFG::SpeculativeJIT::addCallArgument): Renamed argumentIndex to
argumentOffset to match CallFrame naming.

(JSC::DFG::SpeculativeJIT::valueSourceReferenceForOperand): Use helper
functions, as above.

  • interpreter/CallFrame.h:

(JSC::ExecState::argumentOffset):
(JSC::ExecState::argumentOffsetIncludingThis):
(JSC::ExecState::argument):
(JSC::ExecState::setArgument):
(JSC::ExecState::thisArgumentOffset):
(JSC::ExecState::thisValue):
(JSC::ExecState::setThisValue):
(JSC::ExecState::offsetFor):
(JSC::ExecState::hostThisRegister):
(JSC::ExecState::hostThisValue): Added a bunch of helper functions for
computing where an argument is in the register file. Anything in the
runtime that needs to access arguments should use these helpers.

  • interpreter/CallFrameClosure.h:

(JSC::CallFrameClosure::setThis):
(JSC::CallFrameClosure::setArgument):
(JSC::CallFrameClosure::resetCallFrame): This stuff is a lot simpler, now
that too many arguments counts as an arity match and doesn't require
preserving two copies of our arguments.

  • interpreter/Interpreter.cpp:

(JSC::Interpreter::slideRegisterWindowForCall): Only need to do something
special if the caller provided too few arguments.

Key simplification: We never need to maintain two copies of our arguments
anymore.

(JSC::eval):
(JSC::loadVarargs): Use helper functions.

(JSC::Interpreter::unwindCallFrame): Updated for new interface.

(JSC::Interpreter::execute):
(JSC::Interpreter::executeCall):
(JSC::Interpreter::executeConstruct):
(JSC::Interpreter::prepareForRepeatCall): Seriously, though: use helper
functions.

(JSC::Interpreter::privateExecute): No need to check for stack overflow
when calling host functions because they have zero callee registers.

(JSC::Interpreter::retrieveArguments): Explicitly tear off the arguments
object, since there's no special constructor for this anymore.

  • interpreter/Interpreter.h: Reduced the C++ re-entry depth because some

workers tests were hitting stack overflow in some of my testing. We should
make this test more exact in future.

  • interpreter/RegisterFile.h: Death to all runtime knowledge of argument

location that does not belong to the CallFrame class!

  • jit/JIT.cpp:

(JSC::JIT::privateCompile): I am a broken record and I use helper functions.

Also, the whole point of this patch: Treat too many arguments as an arity match.

  • jit/JITCall32_64.cpp:

(JSC::JIT::compileLoadVarargs):

  • jit/JITCall.cpp:

(JSC::JIT::compileLoadVarargs): Updated the argument copying math to use
helper functions, for backwards-correctness. Removed the condition
pertaining to declared argument count because, now that arguments are
always in just one place, this optimization is valid for all functions.
Standardized the if predicate for each line of the optimization. This might
fix a bug, but I couldn't get the bug to crash in practice.

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emit_op_create_arguments):
(JSC::JIT::emit_op_get_argument_by_val):
(JSC::JIT::emitSlow_op_get_argument_by_val):

  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_create_arguments):
(JSC::JIT::emit_op_get_argument_by_val):
(JSC::JIT::emitSlow_op_get_argument_by_val): Removed cti_op_create_arguments_no_params
optimization because it's no longer an optimization, now that arguments
are always contiguous in a known location.

Updated argument access opcode math for backwards-correctness.

  • jit/JITStubs.cpp:

(JSC::arityCheckFor): Updated just like slideRegisterWindowForCall. This
function is slightly different because it copies the call frame in
addition to the arguments. (In the Interpreter, the call frame is not
set up by this point.)

(JSC::lazyLinkFor): The whole point of this patch: Treat too many
arguments as an arity match.

(JSC::DEFINE_STUB_FUNCTION): Updated for new iterface to tearOff().

  • jit/JITStubs.h:
  • jit/SpecializedThunkJIT.h:

(JSC::SpecializedThunkJIT::loadDoubleArgument):
(JSC::SpecializedThunkJIT::loadCellArgument):
(JSC::SpecializedThunkJIT::loadInt32Argument): Use helper functions! They
build strong bones and teeth!

  • runtime/ArgList.cpp:

(JSC::ArgList::getSlice):
(JSC::MarkedArgumentBuffer::slowAppend):

  • runtime/ArgList.h:

(JSC::MarkedArgumentBuffer::MarkedArgumentBuffer):
(JSC::MarkedArgumentBuffer::~MarkedArgumentBuffer):
(JSC::MarkedArgumentBuffer::at):
(JSC::MarkedArgumentBuffer::clear):
(JSC::MarkedArgumentBuffer::append):
(JSC::MarkedArgumentBuffer::removeLast):
(JSC::MarkedArgumentBuffer::last):
(JSC::ArgList::ArgList):
(JSC::ArgList::at): Updated for backwards-correctness. WTF::Vector doesn't
play nice with backwards-ness, so I changed to using manual allocation.

Fixed a FIXME about not all values being marked in the case of out-of-line
arguments. I had to rewrite the loop anyway, and I didn't feel like
maintaining fidelity to its old bugs.

  • runtime/Arguments.cpp:

(JSC::Arguments::visitChildren):
(JSC::Arguments::copyToArguments):
(JSC::Arguments::fillArgList):
(JSC::Arguments::getOwnPropertySlotByIndex):
(JSC::Arguments::getOwnPropertySlot):
(JSC::Arguments::getOwnPropertyDescriptor):
(JSC::Arguments::putByIndex):
(JSC::Arguments::put):
(JSC::Arguments::tearOff):

  • runtime/Arguments.h:

(JSC::Arguments::create):
(JSC::Arguments::Arguments):
(JSC::Arguments::argument):
(JSC::Arguments::finishCreation): Secondary benefit of this patch: deleted
lots of tricky code designed to maintain two different copies of function
arguments. Now that arguments are always contiguous in one place in memory,
this complexity can go away.

Reduced down to one create function for the Arguments class, from three.

Moved tearOff() into an out-of-line function because it's huge.

Moved logic about whether to tear off eagerly into the Arguments class,
so we didn't have to duplicate it elsewhere.

  • runtime/JSActivation.cpp:

(JSC::JSActivation::JSActivation):
(JSC::JSActivation::visitChildren): Renamed m_numParametersMinusThis to
m_numCapturedArgs because if the value really were m_numParametersMinusThis
we would be marking too much. (We shouldn't mark 'this' because it can't
be captured.) Also, use helper functions.

  • runtime/JSActivation.h:

(JSC::JSActivation::tearOff): Use helper functions.

  • runtime/JSArray.cpp:

(JSC::JSArray::copyToArguments):

  • runtime/JSArray.h: Use helper functions, as above.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/Arguments.h

    r100224 r102545  
    4040        WriteBarrier<JSActivation> activation;
    4141
    42         unsigned numParameters;
    43         ptrdiff_t firstParameterIndex;
    4442        unsigned numArguments;
    4543
     
    4745        OwnArrayPtr<WriteBarrier<Unknown> > registerArray;
    4846
    49         WriteBarrier<Unknown>* extraArguments;
    5047        OwnArrayPtr<bool> deletedArguments;
    51         WriteBarrier<Unknown> extraArgumentsFixedBuffer[4];
    5248
    5349        WriteBarrier<JSFunction> callee;
     
    5652        bool overrodeCaller : 1;
    5753        bool isStrictMode : 1;
    58         bool isInlineFrame : 1; // If true, all arguments are in the extraArguments buffer.
    5954    };
    60 
    6155
    6256    class Arguments : public JSNonFinalObject {
     
    7064            return arguments;
    7165        }
    72        
    73         static Arguments* createAndTearOff(JSGlobalData& globalData, CallFrame* callFrame)
    74         {
    75             Arguments* arguments = new (allocateCell<Arguments>(globalData.heap)) Arguments(callFrame);
    76             arguments->finishCreationAndTearOff(callFrame);
    77             return arguments;
    78         }
    79        
    80         static Arguments* createNoParameters(JSGlobalData& globalData, CallFrame* callFrame)
    81         {
    82             Arguments* arguments = new (allocateCell<Arguments>(globalData.heap)) Arguments(callFrame, NoParameters);
    83             arguments->finishCreation(callFrame, NoParameters);
    84             return arguments;
    85         }
    8666
    87         // Use an enum because otherwise gcc insists on doing a memory
    88         // read.
    8967        enum { MaxArguments = 0x10000 };
    9068
     
    9674
    9775    public:
    98         virtual ~Arguments();
    99 
    10076        static const ClassInfo s_info;
    10177
     
    11187        }
    11288       
    113         void copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize);
    114         void tearOff(JSGlobalData&);
     89        void copyToArguments(ExecState*, CallFrame*, uint32_t length);
     90        void tearOff(CallFrame*);
    11591        bool isTornOff() const { return d->registerArray; }
    11692        void didTearOffActivation(JSGlobalData& globalData, JSActivation* activation)
     
    130106        static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags;
    131107
    132         void finishCreationButDontTearOff(CallFrame*);
    133108        void finishCreation(CallFrame*);
    134         void finishCreationAndTearOff(CallFrame*);
    135         void finishCreation(CallFrame*, NoParametersType);
    136109
    137110    private:
    138         void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc);
    139111        static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&);
    140112        static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
     
    147119        void createStrictModeCallerIfNecessary(ExecState*);
    148120        void createStrictModeCalleeIfNecessary(ExecState*);
     121
     122        WriteBarrier<Unknown>& argument(size_t);
    149123
    150124        void init(CallFrame*);
     
    161135    }
    162136
    163     ALWAYS_INLINE void Arguments::getArgumentsData(CallFrame* callFrame, JSFunction*& function, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc)
    164     {
    165         function = asFunction(callFrame->callee());
    166 
    167         int numParameters = function->jsExecutable()->parameterCount();
    168         argc = callFrame->argumentCountIncludingThis();
    169        
    170         if (callFrame->isInlineCallFrame())
    171             ASSERT(argc == numParameters + 1);
    172 
    173         if (argc <= numParameters)
    174             argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters;
    175         else
    176             argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters - argc;
    177 
    178         argc -= 1; // - 1 to skip "this"
    179         firstParameterIndex = -RegisterFile::CallFrameHeaderSize - numParameters;
    180     }
    181 
    182137    inline Arguments::Arguments(CallFrame* callFrame)
    183138        : JSNonFinalObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure())
     
    191146    {
    192147    }
    193    
    194     inline void Arguments::finishCreationButDontTearOff(CallFrame* callFrame)
     148
     149    inline WriteBarrier<Unknown>& Arguments::argument(size_t i)
     150    {
     151        return d->registers[CallFrame::argumentOffset(i)];
     152    }
     153
     154    inline void Arguments::finishCreation(CallFrame* callFrame)
    195155    {
    196156        Base::finishCreation(callFrame->globalData());
    197157        ASSERT(inherits(&s_info));
    198158
    199         JSFunction* callee;
    200         ptrdiff_t firstParameterIndex;
    201         Register* argv;
    202         int numArguments;
    203         getArgumentsData(callFrame, callee, firstParameterIndex, argv, numArguments);
    204 
    205         d->numParameters = callee->jsExecutable()->parameterCount();
    206         d->firstParameterIndex = firstParameterIndex;
    207         d->numArguments = numArguments;
    208         d->isInlineFrame = false;
    209 
     159        JSFunction* callee = asFunction(callFrame->callee());
     160        d->numArguments = callFrame->argumentCount();
    210161        d->registers = reinterpret_cast<WriteBarrier<Unknown>*>(callFrame->registers());
    211 
    212         WriteBarrier<Unknown>* extraArguments;
    213         if (d->numArguments <= d->numParameters)
    214             extraArguments = 0;
    215         else {
    216             unsigned numExtraArguments = d->numArguments - d->numParameters;
    217             if (numExtraArguments > sizeof(d->extraArgumentsFixedBuffer) / sizeof(WriteBarrier<Unknown>))
    218                 extraArguments = new WriteBarrier<Unknown>[numExtraArguments];
    219             else
    220                 extraArguments = d->extraArgumentsFixedBuffer;
    221             for (unsigned i = 0; i < numExtraArguments; ++i)
    222                 extraArguments[i].set(callFrame->globalData(), this, argv[d->numParameters + i].jsValue());
    223         }
    224 
    225         d->extraArguments = extraArguments;
    226 
    227162        d->callee.set(callFrame->globalData(), this, callee);
    228163        d->overrodeLength = false;
     
    230165        d->overrodeCaller = false;
    231166        d->isStrictMode = callFrame->codeBlock()->isStrictMode();
    232     }
    233167
    234     inline void Arguments::finishCreation(CallFrame* callFrame)
    235     {
    236         ASSERT(!callFrame->isInlineCallFrame());
    237         finishCreationButDontTearOff(callFrame);
    238         if (d->isStrictMode)
    239             tearOff(callFrame->globalData());
    240     }
    241 
    242     inline void Arguments::finishCreationAndTearOff(CallFrame* callFrame)
    243     {
    244         Base::finishCreation(callFrame->globalData());
    245         ASSERT(inherits(&s_info));
    246        
    247         JSFunction* callee;
    248 
    249         ptrdiff_t firstParameterIndex;
    250         Register* argv;
    251         int numArguments;
    252         getArgumentsData(callFrame, callee, firstParameterIndex, argv, numArguments);
    253        
    254         d->numParameters = callee->jsExecutable()->parameterCount();
    255         d->firstParameterIndex = firstParameterIndex;
    256         d->numArguments = numArguments;
    257        
    258         if (d->numParameters) {
    259             int registerOffset = d->numParameters + RegisterFile::CallFrameHeaderSize;
    260             size_t registerArraySize = d->numParameters;
    261            
    262             OwnArrayPtr<WriteBarrier<Unknown> > registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[registerArraySize]);
    263             if (callFrame->isInlineCallFrame()) {
    264                 InlineCallFrame* inlineCallFrame = callFrame->inlineCallFrame();
    265                 for (size_t i = 0; i < registerArraySize; ++i) {
    266                     ValueRecovery& recovery = inlineCallFrame->arguments[i + 1];
    267                     // In the future we'll support displaced recoveries (indicating that the
    268                     // argument was flushed to a different location), but for now we don't do
    269                     // that so this code will fail if that were to happen. On the other hand,
    270                     // it's much less likely that we'll support in-register recoveries since
    271                     // this code does not (easily) have access to registers.
    272                     JSValue value;
    273                     Register* location = callFrame->registers() + i - registerOffset;
    274                     switch (recovery.technique()) {
    275                     case AlreadyInRegisterFile:
    276                         value = location->jsValue();
    277                         break;
    278                     case AlreadyInRegisterFileAsUnboxedInt32:
    279                         value = jsNumber(location->unboxedInt32());
    280                         break;
    281                     case AlreadyInRegisterFileAsUnboxedCell:
    282                         value = location->unboxedCell();
    283                         break;
    284                     case AlreadyInRegisterFileAsUnboxedBoolean:
    285                         value = jsBoolean(location->unboxedBoolean());
    286                         break;
    287                     case Constant:
    288                         value = recovery.constant();
    289                         break;
    290                     default:
    291                         ASSERT_NOT_REACHED();
    292                         break;
    293                     }
    294                     registerArray[i].set(callFrame->globalData(), this, value);
    295                 }
    296             } else {
    297                 for (size_t i = 0; i < registerArraySize; ++i)
    298                     registerArray[i].set(callFrame->globalData(), this, callFrame->registers()[i - registerOffset].jsValue());
    299             }
    300             d->registers = registerArray.get() + d->numParameters + RegisterFile::CallFrameHeaderSize;
    301             d->registerArray = registerArray.release();
    302         }
    303        
    304         WriteBarrier<Unknown>* extraArguments;
    305         if (callFrame->isInlineCallFrame())
    306             ASSERT(d->numArguments == d->numParameters);
    307         if (d->numArguments <= d->numParameters)
    308             extraArguments = 0;
    309         else {
    310             unsigned numExtraArguments = d->numArguments - d->numParameters;
    311             if (numExtraArguments > sizeof(d->extraArgumentsFixedBuffer) / sizeof(WriteBarrier<Unknown>))
    312                 extraArguments = new WriteBarrier<Unknown>[numExtraArguments];
    313             else
    314                 extraArguments = d->extraArgumentsFixedBuffer;
    315             for (unsigned i = 0; i < numExtraArguments; ++i)
    316                 extraArguments[i].set(callFrame->globalData(), this, argv[d->numParameters + i].jsValue());
    317         }
    318        
    319         d->extraArguments = extraArguments;
    320 
    321         d->callee.set(callFrame->globalData(), this, callee);
    322         d->overrodeLength = false;
    323         d->overrodeCallee = false;
    324         d->overrodeCaller = false;
    325         d->isInlineFrame = callFrame->isInlineCallFrame();
    326         d->isStrictMode = callFrame->codeBlock()->isStrictMode();
    327     }
    328 
    329     inline void Arguments::finishCreation(CallFrame* callFrame, NoParametersType)
    330     {
    331         ASSERT(!callFrame->isInlineCallFrame());
    332         Base::finishCreation(callFrame->globalData());
    333         ASSERT(inherits(&s_info));
    334         ASSERT(!asFunction(callFrame->callee())->jsExecutable()->parameterCount());
    335 
    336         unsigned numArguments = callFrame->argumentCount();
    337 
    338         d->numParameters = 0;
    339         d->numArguments = numArguments;
    340         d->isInlineFrame = false;
    341 
    342         WriteBarrier<Unknown>* extraArguments;
    343         if (numArguments > sizeof(d->extraArgumentsFixedBuffer) / sizeof(Register))
    344             extraArguments = new WriteBarrier<Unknown>[numArguments];
    345         else
    346             extraArguments = d->extraArgumentsFixedBuffer;
    347 
    348         Register* argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numArguments - 1;
    349         for (unsigned i = 0; i < numArguments; ++i)
    350             extraArguments[i].set(callFrame->globalData(), this, argv[i].jsValue());
    351 
    352         d->extraArguments = extraArguments;
    353 
    354         d->callee.set(callFrame->globalData(), this, asFunction(callFrame->callee()));
    355         d->overrodeLength = false;
    356         d->overrodeCallee = false;
    357         d->overrodeCaller = false;
    358         d->isStrictMode = callFrame->codeBlock()->isStrictMode();
    359         if (d->isStrictMode)
    360             tearOff(callFrame->globalData());
    361     }
    362 
    363     inline void Arguments::tearOff(JSGlobalData& globalData)
    364     {
    365         ASSERT(!isTornOff());
    366 
    367         if (!d->numParameters)
    368             return;
    369 
    370         int registerOffset = d->numParameters + RegisterFile::CallFrameHeaderSize;
    371         size_t registerArraySize = d->numParameters;
    372 
    373         OwnArrayPtr<WriteBarrier<Unknown> > registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[registerArraySize]);
    374         for (size_t i = 0; i < registerArraySize; i++)
    375             registerArray[i].set(globalData, this, d->registers[i - registerOffset].get());
    376         d->registers = registerArray.get() + registerOffset;
    377         d->registerArray = registerArray.release();
     168        // The bytecode generator omits op_tear_off_activation in cases of no
     169        // declared parameters, so we need to tear off immediately.
     170        if (d->isStrictMode || !callee->jsExecutable()->parameterCount())
     171            tearOff(callFrame);
    378172    }
    379173
Note: See TracChangeset for help on using the changeset viewer.