Changeset 42337 in webkit for trunk/JavaScriptCore/parser


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.

Location:
trunk/JavaScriptCore/parser
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/parser/Grammar.y

    r42065 r42337  
    19291929    if (dot->identifier() == GLOBAL_DATA->propertyNames->call)
    19301930        node = new CallFunctionCallDotNode(GLOBAL_DATA, dot->base(), dot->identifier(), args.m_node, divot, divot - start, end - divot);
     1931    else if (dot->identifier() == GLOBAL_DATA->propertyNames->apply)
     1932        node = new ApplyFunctionCallDotNode(GLOBAL_DATA, dot->base(), dot->identifier(), args.m_node, divot, divot - start, end - divot);
    19311933    else
    19321934        node = new FunctionCallDotNode(GLOBAL_DATA, dot->base(), dot->identifier(), args.m_node, divot, divot - start, end - divot);
  • 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());
  • trunk/JavaScriptCore/parser/Nodes.h

    r42065 r42337  
    4444namespace JSC {
    4545
     46    class ArgumentListNode;
    4647    class CodeBlock;
    4748    class BytecodeGenerator;
     
    172173        virtual bool isDotAccessorNode() const JSC_FAST_CALL { return false; }
    173174        virtual bool isFuncExprNode() const JSC_FAST_CALL { return false; }
     175        virtual bool isSimpleArray() const JSC_FAST_CALL { return false; }
    174176
    175177        virtual ExpressionNode* stripUnaryPlus() { return this; }
     
    471473        virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) JSC_FAST_CALL;
    472474
     475        virtual bool isSimpleArray() const JSC_FAST_CALL;
     476        PassRefPtr<ArgumentListNode> toArgumentList(JSGlobalData*) const;
    473477    private:
    474478        RefPtr<ElementNode> m_element;
     
    774778        CallFunctionCallDotNode(JSGlobalData* globalData, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL
    775779            : FunctionCallDotNode(globalData, base, ident, args, divot, startOffset, endOffset)
     780        {
     781        }
     782        virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) JSC_FAST_CALL;
     783    };
     784   
     785    class ApplyFunctionCallDotNode : public FunctionCallDotNode {
     786    public:
     787        ApplyFunctionCallDotNode(JSGlobalData* globalData, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL
     788        : FunctionCallDotNode(globalData, base, ident, args, divot, startOffset, endOffset)
    776789        {
    777790        }
Note: See TracChangeset for help on using the changeset viewer.