Ignore:
Timestamp:
May 18, 2010, 10:10:11 PM (15 years ago)
Author:
[email protected]
Message:

JavaScriptCore: Simplified handling of 'arguments' -- 1.2% SunSpider speedup
https://bugs.webkit.org/show_bug.cgi?id=39200

Reviewed by Darin Adler.

Removed the reserved OptionalCalleeArguments slot from the CallFrame.
Now, slots for 'arguments' are allocated and initialized only by
functions that might need them.

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dump): Updated for new bytecode operands.

(JSC::CodeBlock::CodeBlock):

  • bytecode/CodeBlock.h:

(JSC::unmodifiedArgumentsRegister): Added a helper function for mapping
from the arguments register to its unmodified counterpart.

(JSC::CodeBlock::setArgumentsRegister):
(JSC::CodeBlock::argumentsRegister):
(JSC::CodeBlock::usesArguments): Changed from a "usesArguments" bool to
an optional int index representing the arguments register.

  • bytecode/Opcode.h: Updated for new bytecode operands.
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::addVar): Factored out a helper function for
allocating an anonymous var.

(JSC::BytecodeGenerator::BytecodeGenerator): Merged / simplified some
arguments vs activation logic, and added code to allocate the arguments
registers when needed.

(JSC::BytecodeGenerator::createArgumentsIfNecessary): Updated for new bytecode operands.

(JSC::BytecodeGenerator::emitCallEval): No need to create the arguments
object before calling eval; the activation object will lazily create the
arguments object if eval resolves it.

(JSC::BytecodeGenerator::emitReturn): Updated for new bytecode operands.

(JSC::BytecodeGenerator::emitPushScope):
(JSC::BytecodeGenerator::emitPushNewScope): Ditto emitCallEval.

  • bytecompiler/BytecodeGenerator.h:

(JSC::BytecodeGenerator::addVar): Factored out a helper function for
allocating an anonymous var.

(JSC::BytecodeGenerator::registerFor): No more need for special handling
of the arguments registers; they're allocated just like normal registers
now.

  • interpreter/CallFrame.h:

(JSC::ExecState::callerFrame):
(JSC::ExecState::init):

  • interpreter/CallFrameClosure.h:

(JSC::CallFrameClosure::resetCallFrame): Nixed optionalCalleeArguments.

  • interpreter/Interpreter.cpp:

(JSC::Interpreter::dumpRegisters):
(JSC::Interpreter::unwindCallFrame):
(JSC::Interpreter::privateExecute):
(JSC::Interpreter::retrieveArguments): Opcodes accessing 'arguments' now
take operands specifying registers, just like all other opcodes.
JSActivation::copyRegisters is no longer responsible for tearing off the
arguments object; instead, the VM is responsible for both.

Also, a behavior change: Each access to f.arguments creates a new object,
unless f itself uses 'arguments'. This matches Chrome, and is necessary
for the optimization. f.arguments is a nonstandard, deprecated feature,
so high fidelity to a given implementation is not necessarily a goal.
Also, as illustrated by the new test case, the identity of f.arguments
has been broken since 2008, except in the case where f itself accesses
f.arguments -- but nobody seemed to notice. So, hopefully this change won't
break the web.

  • interpreter/Register.h: Nixed the special arguments accessor. It's no

longer needed.

  • interpreter/RegisterFile.h:

(JSC::RegisterFile::):

  • jit/JITCall.cpp:

(JSC::JIT::compileOpCallInitializeCallFrame):
(JSC::JIT::compileOpCall):

  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_tear_off_activation):
(JSC::JIT::emit_op_tear_off_arguments):
(JSC::JIT::emit_op_create_arguments):
(JSC::JIT::emit_op_init_arguments):

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emit_op_tear_off_activation):
(JSC::JIT::emit_op_tear_off_arguments):
(JSC::JIT::emit_op_create_arguments):
(JSC::JIT::emit_op_init_arguments): The actual optimization: Removed
OptionalCalleeArguments from the callframe slot. Now, it doesn't need
to be initialized for most calls.

  • jit/JITStubs.cpp:

(JSC::DEFINE_STUB_FUNCTION):

  • jit/JITStubs.h:

(JSC::): Updated stubs to support arbitrary 'arguments' registers,
instead of hard-coding something in the call frame.

  • runtime/Arguments.h:

(JSC::JSActivation::copyRegisters): Removed some obfuscatory abstraction.

  • runtime/Executable.h:

(JSC::FunctionExecutable::generatedByteCode): Added a helper for accessing
the 'arguments' register. In a future patch, that kind of data should
probably move out of CodeBlock and into Executable.

  • runtime/JSActivation.cpp:

(JSC::JSActivation::getOwnPropertySlot):
(JSC::JSActivation::argumentsGetter):

  • runtime/JSActivation.h: Simplified / fixed access to 'arguments' via

the activation object. It now implements the same behavior implemented
by optimized variable access in the VM. This simplifies some other
things, too -- like eval code generation.

LayoutTests: Simplified handling of 'arguments' -- 1.2% SunSpider speedup
https://bugs.webkit.org/show_bug.cgi?id=39200

Reviewed by Darin Adler.

  • fast/js/function-dot-arguments-expected.txt:
  • fast/js/script-tests/function-dot-arguments.js:

(argumentsIdentity): Updated to match new behavior.

  • fast/js/function-dot-arguments2-expected.txt:
  • fast/js/function-dot-arguments2.html: New tests for some things that

weren't covered before.

  • fast/js/global-recursion-on-full-stack.html: Rejiggered the stack

usage in this test. Since stack usage is more efficient now, you
need a slightly different usage pattern to hit the exact thing this
test wanted to test.

  • fast/js/kde/script-tests/function_arguments.js:

(f): Updated to more specifically test what this was trying to test,
to avoid just testing the identity of f.arguments.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/interpreter/Interpreter.cpp

    r59339 r59742  
    468468    printf("[ArgumentCount]            | %10p | %d \n", it, (*it).i()); ++it;
    469469    printf("[Callee]                   | %10p | %p \n", it, (*it).function()); ++it;
    470     printf("[OptionalCalleeArguments]  | %10p | %p \n", it, (*it).arguments()); ++it;
    471470    printf("-----------------------------------------------------------------------------\n");
    472471
     
    541540        while (!scopeChain->object->inherits(&JSActivation::info))
    542541            scopeChain = scopeChain->pop();
    543         static_cast<JSActivation*>(scopeChain->object)->copyRegisters(callFrame->optionalCalleeArguments());
    544     } else if (Arguments* arguments = callFrame->optionalCalleeArguments()) {
    545         if (!arguments->isTornOff())
    546             arguments->copyRegisters();
     542        JSActivation* activation = asActivation(scopeChain->object);
     543        activation->copyRegisters();
     544        if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue())
     545            asArguments(arguments)->setActivation(activation);
     546    } else if (oldCodeBlock->usesArguments()) {
     547        if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue())
     548            asArguments(arguments)->copyRegisters();
    547549    }
    548550
     
    37773779    }
    37783780    DEFINE_OPCODE(op_tear_off_activation) {
    3779         /* tear_off_activation activation(r)
    3780 
    3781            Copy all locals and parameters to new memory allocated on
    3782            the heap, and make the passed activation use this memory
    3783            in the future when looking up entries in the symbol table.
    3784            If there is an 'arguments' object, then it will also use
    3785            this memory for storing the named parameters, but not any
    3786            extra arguments.
    3787 
    3788            This opcode should only be used immediately before op_ret.
    3789         */
    3790 
    3791         int src = vPC[1].u.operand;
     3781        /* tear_off_activation activation(r) arguments(r)
     3782
     3783           Copy locals and named parameters from the register file to the heap.
     3784           Point the bindings in 'activation' and 'arguments' to this new backing
     3785           store. (Note that 'arguments' may not have been created. If created,
     3786           'arguments' already holds a copy of any extra / unnamed parameters.)
     3787
     3788           This opcode appears before op_ret in functions that require full scope chains.
     3789        */
     3790
     3791        int src1 = vPC[1].u.operand;
     3792        int src2 = vPC[2].u.operand;
    37923793        ASSERT(callFrame->codeBlock()->needsFullScopeChain());
    37933794
    3794         asActivation(callFrame->r(src).jsValue())->copyRegisters(callFrame->optionalCalleeArguments());
     3795        JSActivation* activation = asActivation(callFrame->r(src1).jsValue());
     3796        activation->copyRegisters();
     3797
     3798        if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(src2)).jsValue())
     3799            asArguments(arguments)->setActivation(activation);
    37953800
    37963801        vPC += OPCODE_LENGTH(op_tear_off_activation);
     
    37983803    }
    37993804    DEFINE_OPCODE(op_tear_off_arguments) {
    3800         /* tear_off_arguments
    3801 
    3802            Copy all arguments to new memory allocated on the heap,
    3803            and make the 'arguments' object use this memory in the
    3804            future when looking up named parameters, but not any
    3805            extra arguments. If an activation object exists for the
    3806            current function context, then the tear_off_activation
    3807            opcode should be used instead.
    3808 
    3809            This opcode should only be used immediately before op_ret.
    3810         */
    3811 
    3812         ASSERT(callFrame->codeBlock()->usesArguments() && !callFrame->codeBlock()->needsFullScopeChain());
    3813 
    3814         if (callFrame->optionalCalleeArguments())
    3815             callFrame->optionalCalleeArguments()->copyRegisters();
     3805        /* tear_off_arguments arguments(r)
     3806
     3807           Copy named parameters from the register file to the heap. Point the
     3808           bindings in 'arguments' to this new backing store. (Note that
     3809           'arguments' may not have been created. If created, 'arguments' already
     3810           holds a copy of any extra / unnamed parameters.)
     3811
     3812           This opcode appears before op_ret in functions that don't require full
     3813           scope chains, but do use 'arguments'.
     3814        */
     3815
     3816        int src1 = vPC[1].u.operand;
     3817        ASSERT(!callFrame->codeBlock()->needsFullScopeChain() && callFrame->codeBlock()->usesArguments());
     3818
     3819        if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(src1)).jsValue())
     3820            asArguments(arguments)->copyRegisters();
    38163821
    38173822        vPC += OPCODE_LENGTH(op_tear_off_arguments);
     
    38493854        /* enter
    38503855
    3851            Initializes local variables to undefined and fills constant
    3852            registers with their values. If the code block requires an
    3853            activation, enter_with_activation should be used instead.
    3854 
    3855            This opcode should only be used at the beginning of a code
    3856            block.
     3856           Initializes local variables to undefined. If the code block requires
     3857           an activation, enter_with_activation is used instead.
     3858
     3859           This opcode appears only at the beginning of a code block.
    38573860        */
    38583861
     
    38693872        /* enter_with_activation dst(r)
    38703873
    3871            Initializes local variables to undefined, fills constant
    3872            registers with their values, creates an activation object,
    3873            and places the new activation both in dst and at the top
    3874            of the scope chain. If the code block does not require an
    3875            activation, enter should be used instead.
    3876 
    3877            This opcode should only be used at the beginning of a code
    3878            block.
     3874           Initializes local variables to undefined, creates an activation object,
     3875           places it in dst, and pushes it onto the scope chain.
     3876
     3877           This opcode appears only at the beginning of a code block.
    38793878        */
    38803879
     
    39143913    }
    39153914    DEFINE_OPCODE(op_init_arguments) {
    3916         /* create_arguments
    3917 
    3918            Initialises the arguments object reference to null to ensure
    3919            we can correctly detect that we need to create it later (or
    3920            avoid creating it altogether).
    3921 
    3922            This opcode should only be used at the beginning of a code
    3923            block.
     3915        /* create_arguments dst(r)
     3916
     3917           Initialises 'arguments' to JSValue().
     3918
     3919           This opcode appears only at the beginning of a code block.
    39243920         */
    3925         callFrame->r(RegisterFile::ArgumentsRegister) = JSValue();
     3921        int dst = vPC[1].u.operand;
     3922
     3923        callFrame->r(dst) = JSValue();
     3924        callFrame->r(unmodifiedArgumentsRegister(dst)) = JSValue();
    39263925        vPC += OPCODE_LENGTH(op_init_arguments);
    39273926        NEXT_INSTRUCTION();
    39283927    }
    39293928    DEFINE_OPCODE(op_create_arguments) {
    3930         /* create_arguments
     3929        /* create_arguments dst(r)
    39313930
    39323931           Creates the 'arguments' object and places it in both the
     
    39353934         */
    39363935       
    3937          if (!callFrame->r(RegisterFile::ArgumentsRegister).jsValue()) {
    3938              Arguments* arguments = new (globalData) Arguments(callFrame);
    3939              callFrame->setCalleeArguments(arguments);
    3940              callFrame->r(RegisterFile::ArgumentsRegister) = JSValue(arguments);
    3941          }
     3936        int dst = vPC[1].u.operand;
     3937
     3938        if (!callFrame->r(dst).jsValue()) {
     3939            Arguments* arguments = new (globalData) Arguments(callFrame);
     3940            callFrame->r(dst) = JSValue(arguments);
     3941            callFrame->r(unmodifiedArgumentsRegister(dst)) = JSValue(arguments);
     3942        }
    39423943        vPC += OPCODE_LENGTH(op_create_arguments);
    39433944        NEXT_INSTRUCTION();
     
    44204421    if (codeBlock->usesArguments()) {
    44214422        ASSERT(codeBlock->codeType() == FunctionCode);
    4422         SymbolTable& symbolTable = *codeBlock->symbolTable();
    4423         int argumentsIndex = symbolTable.get(functionCallFrame->propertyNames().arguments.ustring().rep()).getIndex();
    4424         if (!functionCallFrame->r(argumentsIndex).jsValue()) {
    4425             Arguments* arguments = new (callFrame) Arguments(functionCallFrame);
    4426             functionCallFrame->setCalleeArguments(arguments);
    4427             functionCallFrame->r(RegisterFile::ArgumentsRegister) = JSValue(arguments);
    4428         }
    4429         return functionCallFrame->r(argumentsIndex).jsValue();
    4430     }
    4431 
    4432     Arguments* arguments = functionCallFrame->optionalCalleeArguments();
    4433     if (!arguments) {
    4434         arguments = new (functionCallFrame) Arguments(functionCallFrame);
    4435         arguments->copyRegisters();
    4436         callFrame->setCalleeArguments(arguments);
    4437     }
    4438 
     4423        int argumentsRegister = codeBlock->argumentsRegister();
     4424        if (!functionCallFrame->r(argumentsRegister).jsValue()) {
     4425            JSValue arguments = JSValue(new (callFrame) Arguments(functionCallFrame));
     4426            functionCallFrame->r(argumentsRegister) = arguments;
     4427            functionCallFrame->r(unmodifiedArgumentsRegister(argumentsRegister)) = arguments;
     4428        }
     4429        return functionCallFrame->r(argumentsRegister).jsValue();
     4430    }
     4431
     4432    Arguments* arguments = new (functionCallFrame) Arguments(functionCallFrame);
     4433    arguments->copyRegisters();
    44394434    return arguments;
    44404435}
Note: See TracChangeset for help on using the changeset viewer.