Ignore:
Timestamp:
Apr 8, 2009, 4:08:28 PM (16 years ago)
Author:
[email protected]
Message:

Improve function.apply performance

Reviewed by Geoff Garen.

Jump through a few hoops to improve performance of function.apply in the general case.

In the case of zero or one arguments, or if there are only two arguments and the
second is an array literal we treat function.apply as function.call.

Otherwise we use the new opcodes op_load_varargs and op_call_varargs to do the .apply call
without re-entering the virtual machine.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/parser/Nodes.cpp

    r42065 r42337  
    405405}
    406406
     407bool ArrayNode::isSimpleArray() const
     408{
     409    if (m_elision || m_optional)
     410        return false;
     411    for (ElementNode* ptr = m_element.get(); ptr; ptr = ptr->next()) {
     412        if (ptr->elision())
     413            return false;
     414    }
     415    return true;
     416}
     417
     418PassRefPtr<ArgumentListNode> ArrayNode::toArgumentList(JSGlobalData* globalData) const
     419{
     420    ASSERT(!m_elision && !m_optional);
     421    RefPtr<ArgumentListNode> head;
     422    ElementNode* ptr = m_element.get();
     423    if (!ptr)
     424        return head;
     425    head = new ArgumentListNode(globalData, ptr->value());
     426    ArgumentListNode* tail = head.get();
     427    ptr = ptr->next();
     428    for (; ptr; ptr = ptr->next()) {
     429        ASSERT(!ptr->elision());
     430        tail = new ArgumentListNode(globalData, tail, ptr->value());
     431    }
     432    return head.release();
     433}
     434
    407435// ------------------------------ PropertyNode ----------------------------
    408436
     
    711739            generator.emitNode(thisRegister.get(), m_args->m_listNode->m_expr.get());
    712740            m_args->m_listNode = m_args->m_listNode->m_next;
    713         } else {
     741        } else
    714742            generator.emitLoad(thisRegister.get(), jsNull());
    715         }
     743
    716744        generator.emitCall(finalDestination.get(), realFunction.get(), thisRegister.get(), m_args.get(), divot(), startOffset(), endOffset());
    717745        generator.emitJump(end.get());
    718746        m_args->m_listNode = oldList;
     747    }
     748    generator.emitLabel(realCall.get());
     749    {
     750        RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get());
     751        generator.emitCall(finalDestination.get(), function.get(), thisRegister.get(), m_args.get(), divot(), startOffset(), endOffset());
     752    }
     753    generator.emitLabel(end.get());
     754    return finalDestination.get();
     755}
     756   
     757static bool areTrivialApplyArguments(ArgumentsNode* args)
     758{
     759    return !args->m_listNode || !args->m_listNode->m_expr || !args->m_listNode->m_next
     760        || (!args->m_listNode->m_next->m_next && args->m_listNode->m_next->m_expr->isSimpleArray());
     761}
     762
     763RegisterID* ApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     764{
     765    // A few simple cases can be trivially handled as ordinary functions calls
     766    // function.apply(), function.apply(arg) -> identical to function.call
     767    // function.apply(thisArg, [arg0, arg1, ...]) -> can be trivially coerced into function.call(thisArg, arg0, arg1, ...) and saves object allocation
     768    bool mayBeCall = areTrivialApplyArguments(m_args.get());
     769
     770    RefPtr<Label> realCall = generator.newLabel();
     771    RefPtr<Label> end = generator.newLabel();
     772    RefPtr<RegisterID> base = generator.emitNode(m_base.get());
     773    generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset);
     774    RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident);
     775    RefPtr<RegisterID> finalDestination = generator.finalDestination(dst, function.get());
     776    generator.emitJumpIfNotFunctionApply(function.get(), realCall.get());
     777    {
     778        if (mayBeCall) {
     779            RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get());
     780            RefPtr<RegisterID> thisRegister = generator.newTemporary();
     781            RefPtr<ArgumentListNode> oldList = m_args->m_listNode;
     782            if (m_args->m_listNode && m_args->m_listNode->m_expr) {
     783                generator.emitNode(thisRegister.get(), m_args->m_listNode->m_expr.get());
     784                m_args->m_listNode = m_args->m_listNode->m_next;
     785                if (m_args->m_listNode) {
     786                    ASSERT(m_args->m_listNode->m_expr->isSimpleArray());
     787                    ASSERT(!m_args->m_listNode->m_next);
     788                    m_args->m_listNode = static_cast<ArrayNode*>(m_args->m_listNode->m_expr.get())->toArgumentList(generator.globalData());
     789                }
     790            } else
     791                generator.emitLoad(thisRegister.get(), jsNull());
     792            generator.emitCall(finalDestination.get(), realFunction.get(), thisRegister.get(), m_args.get(), divot(), startOffset(), endOffset());
     793            m_args->m_listNode = oldList;
     794        } else {
     795            ASSERT(m_args->m_listNode && m_args->m_listNode->m_next);
     796            RefPtr<RegisterID> realFunction = generator.emitMove(generator.newTemporary(), base.get());
     797            RefPtr<RegisterID> argsCountRegister = generator.newTemporary();
     798            RefPtr<RegisterID> thisRegister = generator.newTemporary();
     799            RefPtr<RegisterID> argsRegister = generator.newTemporary();
     800            generator.emitNode(thisRegister.get(), m_args->m_listNode->m_expr.get());
     801            ArgumentListNode* args = m_args->m_listNode->m_next.get();
     802            generator.emitNode(argsRegister.get(), args->m_expr.get());
     803            while ((args = args->m_next.get()))
     804                generator.emitNode(generator.newTemporary(), args->m_expr.get());
     805
     806            generator.emitLoadVarargs(argsCountRegister.get(), argsRegister.get());
     807            generator.emitCallVarargs(finalDestination.get(), realFunction.get(), thisRegister.get(), argsCountRegister.get(), divot(), startOffset(), endOffset());
     808        }
     809        generator.emitJump(end.get());
    719810    }
    720811    generator.emitLabel(realCall.get());
Note: See TracChangeset for help on using the changeset viewer.