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/interpreter/Interpreter.cpp

    r42065 r42337  
    29732973        goto vm_throw;
    29742974    }
     2975    DEFINE_OPCODE(op_load_varargs) {
     2976        int argCountDst = (++vPC)->u.operand;
     2977        int argsOffset = (++vPC)->u.operand;
     2978       
     2979        JSValuePtr arguments = callFrame[argsOffset].jsValue(callFrame);
     2980        uint32_t argCount = 0;
     2981        if (!arguments.isUndefinedOrNull()) {
     2982            if (!arguments.isObject()) {
     2983                exceptionValue = createInvalidParamError(callFrame, "Function.prototype.apply", arguments, vPC - callFrame->codeBlock()->instructions().begin(), callFrame->codeBlock());
     2984                goto vm_throw;
     2985            }
     2986            if (asObject(arguments)->classInfo() == &Arguments::info) {
     2987                Arguments* args = asArguments(arguments);
     2988                argCount = args->numProvidedArguments(callFrame);
     2989                int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize;
     2990                Register* newEnd = callFrame->registers() + sizeDelta;
     2991                if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) {
     2992                    exceptionValue = createStackOverflowError(callFrame);
     2993                    goto vm_throw;
     2994                }
     2995                args->copyToRegisters(callFrame, callFrame->registers() + argsOffset, argCount);
     2996            } else if (isJSArray(&callFrame->globalData(), arguments)) {
     2997                JSArray* array = asArray(arguments);
     2998                argCount = array->length();
     2999                int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize;
     3000                Register* newEnd = callFrame->registers() + sizeDelta;
     3001                if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) {
     3002                    exceptionValue = createStackOverflowError(callFrame);
     3003                    goto vm_throw;
     3004                }
     3005                array->copyToRegisters(callFrame, callFrame->registers() + argsOffset, argCount);
     3006            } else if (asObject(arguments)->inherits(&JSArray::info)) {
     3007                JSObject* argObject = asObject(arguments);
     3008                argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame);
     3009                int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize;
     3010                Register* newEnd = callFrame->registers() + sizeDelta;
     3011                if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) {
     3012                    exceptionValue = createStackOverflowError(callFrame);
     3013                    goto vm_throw;
     3014                }
     3015                Register* argsBuffer = callFrame->registers() + argsOffset;
     3016                for (unsigned i = 0; i < argCount; ++i) {
     3017                    argsBuffer[i] = asObject(arguments)->get(callFrame, i);
     3018                    CHECK_FOR_EXCEPTION();
     3019                }
     3020            } else {
     3021                if (!arguments.isObject()) {
     3022                    exceptionValue = createInvalidParamError(callFrame, "Function.prototype.apply", arguments, vPC - callFrame->codeBlock()->instructions().begin(), callFrame->codeBlock());
     3023                    goto vm_throw;
     3024                }
     3025            }
     3026        }
     3027        CHECK_FOR_EXCEPTION();
     3028        callFrame[argCountDst] = argCount + 1;
     3029        ++vPC;
     3030        NEXT_INSTRUCTION();
     3031    }
     3032    DEFINE_OPCODE(op_call_varargs) {
     3033        /* call_varargs dst(r) func(r) argCountReg(r) baseRegisterOffset(n)
     3034         
     3035         Perform a function call with a dynamic set of arguments.
     3036         
     3037         registerOffset is the distance the callFrame pointer should move
     3038         before the VM initializes the new call frame's header, excluding
     3039         space for arguments.
     3040         
     3041         dst is where op_ret should store its result.
     3042         */
     3043       
     3044        int dst = vPC[1].u.operand;
     3045        int func = vPC[2].u.operand;
     3046        int argCountReg = vPC[3].u.operand;
     3047        int registerOffset = vPC[4].u.operand;
     3048       
     3049        JSValuePtr v = callFrame[func].jsValue(callFrame);
     3050        int argCount = callFrame[argCountReg].i();
     3051        registerOffset += argCount;
     3052        CallData callData;
     3053        CallType callType = v.getCallData(callData);
     3054       
     3055        if (callType == CallTypeJS) {
     3056            ScopeChainNode* callDataScopeChain = callData.js.scopeChain;
     3057            FunctionBodyNode* functionBodyNode = callData.js.functionBody;
     3058            CodeBlock* newCodeBlock = &functionBodyNode->bytecode(callDataScopeChain);
     3059           
     3060            CallFrame* previousCallFrame = callFrame;
     3061           
     3062            callFrame = slideRegisterWindowForCall(newCodeBlock, registerFile, callFrame, registerOffset, argCount);
     3063            if (UNLIKELY(!callFrame)) {
     3064                callFrame = previousCallFrame;
     3065                exceptionValue = createStackOverflowError(callFrame);
     3066                goto vm_throw;
     3067            }
     3068           
     3069            callFrame->init(newCodeBlock, vPC + 5, callDataScopeChain, previousCallFrame, dst, argCount, asFunction(v));
     3070            vPC = newCodeBlock->instructions().begin();
     3071           
     3072#if ENABLE(OPCODE_STATS)
     3073            OpcodeStats::resetLastInstruction();
     3074#endif
     3075           
     3076            NEXT_INSTRUCTION();
     3077        }
     3078       
     3079        if (callType == CallTypeHost) {
     3080            ScopeChainNode* scopeChain = callFrame->scopeChain();
     3081            CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset);
     3082            newCallFrame->init(0, vPC + 5, scopeChain, callFrame, dst, argCount, 0);
     3083           
     3084            Register* thisRegister = newCallFrame->registers() - RegisterFile::CallFrameHeaderSize - argCount;
     3085            ArgList args(thisRegister + 1, argCount - 1);
     3086           
     3087            // FIXME: All host methods should be calling toThisObject, but this is not presently the case.
     3088            JSValuePtr thisValue = thisRegister->jsValue(callFrame);
     3089            if (thisValue == jsNull())
     3090                thisValue = callFrame->globalThisValue();
     3091           
     3092            JSValuePtr returnValue;
     3093            {
     3094                SamplingTool::HostCallRecord callRecord(m_sampler);
     3095                returnValue = callData.native.function(newCallFrame, asObject(v), thisValue, args);
     3096            }
     3097            CHECK_FOR_EXCEPTION();
     3098           
     3099            callFrame[dst] = JSValuePtr(returnValue);
     3100           
     3101            vPC += 5;
     3102            NEXT_INSTRUCTION();
     3103        }
     3104       
     3105        ASSERT(callType == CallTypeNone);
     3106       
     3107        exceptionValue = createNotAFunctionError(callFrame, v, vPC - callFrame->codeBlock()->instructions().begin(), callFrame->codeBlock());
     3108        goto vm_throw;
     3109    }
    29753110    DEFINE_OPCODE(op_tear_off_activation) {
    29763111        /* tear_off_activation activation(r)
Note: See TracChangeset for help on using the changeset viewer.