Ignore:
Timestamp:
Sep 21, 2012, 11:34:59 PM (13 years ago)
Author:
[email protected]
Message:

Optimized closures that capture arguments
https://bugs.webkit.org/show_bug.cgi?id=97358

Reviewed by Oliver Hunt.

Source/JavaScriptCore:

Previously, the activation object was responsible for capturing all
arguments in a way that was convenient for the arguments object. Now,
we move all captured variables into a contiguous region in the stack,
allocate an activation for exactly that size, and make the arguments
object responsible for knowing all the places to which arguments could
have moved.

This seems like the right tradeoff because

(a) Closures are common and long-lived, so we want them to be small.

(b) Our primary strategy for optimizing the arguments object is to make
it go away. If you're allocating arguments objects, you're already having
a bad time.

(c) It's common to use either the arguments object or named argument
closure, but not both.

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dump):
(JSC::CodeBlock::CodeBlock):

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::argumentsRegister):
(JSC::CodeBlock::activationRegister):
(JSC::CodeBlock::isCaptured):
(JSC::CodeBlock::argumentIndexAfterCapture): m_numCapturedVars is gone
now -- we have an explicit range instead.

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::BytecodeGenerator): Move captured arguments
into the captured region of local variables for space efficiency. Record
precise data about where they moved for the sake of the arguments object.

Some of this data was previously wrong, but it didn't cause any problems
because the arguments weren't actually moving.

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::flushArgumentsAndCapturedVariables): Don't
assume that captured vars are in any particular location -- always ask
the CodeBlock. This is better encapsulation.

(JSC::DFG::ByteCodeParser::parseCodeBlock):

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile): I rename things sometimes.

  • runtime/Arguments.cpp:

(JSC::Arguments::tearOff): Account for a particularly nasty edge case.

(JSC::Arguments::didTearOffActivation): Don't allocate our slow arguments
data on tear-off. We need to allocate it eagerly instead, since we need
to know about displaced, captured arguments during access before tear-off.

  • runtime/Arguments.h:

(JSC::Arguments::allocateSlowArguments):
(JSC::Arguments::argument): Tell our slow arguments array where all arguments
are, even if they are not captured. This simplifies some things, so we don't
have to account explicitly for the full matrix of (not torn off, torn off)

  • (captured, not captured).

(JSC::Arguments::finishCreation): Allocate our slow arguments array eagerly
because we need to know about displaced, captured arguments during access
before tear-off.

  • runtime/Executable.cpp:

(JSC::FunctionExecutable::FunctionExecutable):
(JSC::FunctionExecutable::compileForCallInternal):
(JSC::FunctionExecutable::compileForConstructInternal):

  • runtime/Executable.h:

(JSC::FunctionExecutable::parameterCount):
(FunctionExecutable):

  • runtime/JSActivation.cpp:

(JSC::JSActivation::visitChildren):

  • runtime/JSActivation.h:

(JSActivation):
(JSC::JSActivation::create):
(JSC::JSActivation::JSActivation):
(JSC::JSActivation::registerOffset):
(JSC::JSActivation::tearOff):
(JSC::JSActivation::allocationSize):
(JSC::JSActivation::isValid): This is really the point of the patch. All
the pointer math in Activations basically boils away, since we always
copy a contiguous region of captured variables now.

  • runtime/SymbolTable.h:

(JSC::SlowArgument::SlowArgument):
(SlowArgument):
(SharedSymbolTable):
(JSC::SharedSymbolTable::captureCount):
(JSC::SharedSymbolTable::SharedSymbolTable): AllOfTheThings capture mode
is gone now -- that's the point of the patch. indexIfCaptured gets renamed
to index because we always have an index, even if not captured. (The only
time when the index is meaningless is when we're Deleted.)

LayoutTests:

  • fast/js/dfg-arguments-alias-activation-expected.txt:
  • fast/js/dfg-arguments-alias-activation.html:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r129287 r129297  
    305305    // FIXME: Move code that modifies the global object to Interpreter::execute.
    306306   
    307     codeBlock->m_numCapturedVars = codeBlock->m_numVars;
    308    
    309307    if (compilationKind == OptimizingCompilation)
    310308        return;
     
    393391    }
    394392
     393    symbolTable->setCaptureStart(m_codeBlock->m_numVars);
     394
    395395    if (functionBody->usesArguments() || codeBlock->usesEval() || m_shouldEmitDebugHooks) { // May reify arguments object.
    396396        RegisterID* unmodifiedArgumentsRegister = addVar(); // Anonymous, so it can't be modified by user code.
     
    424424    }
    425425
    426     bool mayReifyArgumentsObject = codeBlock->usesArguments() || codeBlock->usesEval() || m_shouldEmitDebugHooks;
     426    bool shouldCaptureAllTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval();
     427
    427428    bool capturesAnyArgumentByName = false;
    428     if (functionBody->hasCapturedVariables()) {
     429    Vector<RegisterID*> capturedArguments;
     430    if (functionBody->hasCapturedVariables() || shouldCaptureAllTheThings) {
    429431        FunctionParameters& parameters = *functionBody->parameters();
     432        capturedArguments.resize(parameters.size());
    430433        for (size_t i = 0; i < parameters.size(); ++i) {
    431             if (!functionBody->captures(parameters[i]))
     434            capturedArguments[i] = 0;
     435            if (!functionBody->captures(parameters[i]) && !shouldCaptureAllTheThings)
    432436                continue;
    433437            capturesAnyArgumentByName = true;
    434             break;
    435         }
    436     }
    437 
    438     if (mayReifyArgumentsObject || capturesAnyArgumentByName) {
    439         symbolTable->setCaptureMode(SharedSymbolTable::AllOfTheThings);
    440         symbolTable->setCaptureStart(-CallFrame::offsetFor(symbolTable->parameterCountIncludingThis()));
    441     } else {
    442         symbolTable->setCaptureMode(SharedSymbolTable::SomeOfTheThings);
    443         symbolTable->setCaptureStart(m_codeBlock->m_numVars);
    444     }
    445 
    446     if (mayReifyArgumentsObject && capturesAnyArgumentByName) {
     438            capturedArguments[i] = addVar();
     439        }
     440    }
     441
     442    if (capturesAnyArgumentByName && !codeBlock->isStrictMode()) {
    447443        size_t parameterCount = symbolTable->parameterCount();
    448444        OwnArrayPtr<SlowArgument> slowArguments = adoptArrayPtr(new SlowArgument[parameterCount]);
    449445        for (size_t i = 0; i < parameterCount; ++i) {
     446            if (!capturedArguments[i]) {
     447                ASSERT(slowArguments[i].status == SlowArgument::Normal);
     448                slowArguments[i].index = CallFrame::argumentOffset(i);
     449                continue;
     450            }
    450451            slowArguments[i].status = SlowArgument::Captured;
    451             slowArguments[i].indexIfCaptured = CallFrame::argumentOffset(i);
     452            slowArguments[i].index = capturedArguments[i]->index();
    452453        }
    453454        symbolTable->setSlowArguments(slowArguments.release());
     
    492493    }
    493494
    494     codeBlock->m_numCapturedVars = codeBlock->m_numVars;
     495    symbolTable->setCaptureEnd(codeBlock->m_numVars);
    495496
    496497    m_firstLazyFunction = codeBlock->m_numVars;
     
    519520    }
    520521
    521     if (m_shouldEmitDebugHooks || codeBlock->usesEval())
    522         codeBlock->m_numCapturedVars = codeBlock->m_numVars;
    523 
    524     symbolTable->setCaptureEnd(codeBlock->m_numCapturedVars);
     522    if (shouldCaptureAllTheThings)
     523        symbolTable->setCaptureEnd(codeBlock->m_numVars);
    525524
    526525    FunctionParameters& parameters = *functionBody->parameters();
     
    532531    m_codeBlock->addParameter();
    533532   
    534     for (size_t i = 0; i < parameters.size(); ++i)
    535         addParameter(parameters[i], nextParameterIndex--);
    536 
     533    for (size_t i = 0; i < parameters.size(); ++i, --nextParameterIndex) {
     534        int index = nextParameterIndex;
     535        if (capturedArguments.size() && capturedArguments[i]) {
     536            ASSERT((functionBody->hasCapturedVariables() && functionBody->captures(parameters[i])) || shouldCaptureAllTheThings);
     537            index = capturedArguments[i]->index();
     538            RegisterID original(nextParameterIndex);
     539            emitMove(capturedArguments[i], &original);
     540        }
     541        addParameter(parameters[i], index);
     542    }
    537543    preserveLastVar();
    538544
     
    604610        variables.append(*varStack[i].first);
    605611    codeBlock->adoptVariables(variables);
    606     codeBlock->m_numCapturedVars = codeBlock->m_numVars;
    607612    preserveLastVar();
    608613}
Note: See TracChangeset for help on using the changeset viewer.