Ignore:
Timestamp:
Oct 31, 2016, 7:56:30 PM (9 years ago)
Author:
[email protected]
Message:

We should be able to eliminate rest parameter allocations
https://bugs.webkit.org/show_bug.cgi?id=163925

Reviewed by Filip Pizlo.

JSTests:

  • microbenchmarks/rest-parameter-allocation-elimination.js: Added.

(assert):
(test1.bar):
(test1):
(test2.jaz):
(test2.jaz2.kaz):
(test2.jaz2):
(test2):
(test3.foo):
(test3.baz):
(test3.jaz):
(test3):
(test4.baz):
(test4.jaz):
(test4):
(test5.baz):
(test5.jaz):
(test5):
(test6.baz):
(test6.jaz):
(test6):
(test7.baz):
(test7.jaz):
(test7.check):
(test7):
(test8.baz):
(test8.jaz):
(test8.check):
(test8):
(test9.baz):
(test9.jaz):
(test9.check):
(test9):
(test10.baz):
(test10.jaz):
(test10):
(test11.bar):
(test11.foo):
(test11.makeArguments):
(test11.):
(test12):
(test12.bar):
(test12.foo):
(test12.makeArguments):
(test12.):
(test13.bar):
(test13.top):
(test13.foo):
(test13.makeArguments):
(test13.):
(test13):
(test14.bar):
(test14.top):
(test14.foo):
(test14.makeArguments):
(test14.):
(test14):
(test15.bar):
(test15.top):
(test15.foo):
(test15.makeArguments):
(test15.):
(test15):

Source/JavaScriptCore:

This is the first step towards eliminating rest parameter
allocations when they're spread to other function calls:
function foo(...args) { bar(...args); }

This patch simply removes the allocation for rest parameter
allocations using the same escape analysis that is performed
in the argument elimination phase. I've added a new rule to
the phase to make sure that CheckStructure doesn't count as
an escape for an allocation since this often shows up in code
like this:

`
function foo(...args) {

let r = [];
for (let i = 0; i < args.length; i++)

r.push(args[i]);

return r;

}
`

The above program now entirely eliminates the allocation for args
compiled in the FTL. Programs like this also eliminate the allocation
for args:

`
function foo(...args) { return [args.length, args[0]]; }

function bar(...args) { return someOtherFunction.apply(null, args); }
`

This patch extends the arguments elimination phase to understand
the concept that we may want to forward arguments, or get from
the arguments region, starting at some offset. The offset is the
number of names parameter before the rest parameter. For example:

`
function foo(a, b, ...args) { return bar.apply(null, args); }
`

Will forward arguments starting at the *third* argument.
Our arguments forwarding code already had the notion of starting
from some offset, however, I found bugs in that code. I extended
it to work properly for rest parameters with arbitrary skip offsets.

And this program:
`
function foo(...args) {

let r = [];
for (let i = 0; i < args.length; i++)

r.push(args[i]);

return r;

}
`

Knows to perform the GetMyArgumentByVal* with an offset of 3
inside the loop. To make this work, I taught GetMyArgumentByVal
and GetMyArgumentByValOutOfBounds to take an offset representing
the number of arguments to skip.

This patch is a ~20% speedup on microbenchmarks.

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::finishCreation):

  • dfg/DFGAbstractInterpreterInlines.h:

(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):

  • dfg/DFGArgumentsEliminationPhase.cpp:
  • dfg/DFGArgumentsUtilities.cpp:

(JSC::DFG::emitCodeToGetArgumentsArrayLength):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGConstantFoldingPhase.cpp:

(JSC::DFG::ConstantFoldingPhase::foldConstants):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGNode.h:

(JSC::DFG::Node::hasConstant):
(JSC::DFG::Node::constant):
(JSC::DFG::Node::isPhantomAllocation):
(JSC::DFG::Node::numberOfArgumentsToSkip):

  • dfg/DFGNodeType.h:
  • dfg/DFGOSRAvailabilityAnalysisPhase.cpp:

(JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):

  • dfg/DFGOperations.cpp:
  • dfg/DFGPreciseLocalClobberize.h:

(JSC::DFG::PreciseLocalClobberizeAdaptor::readTop):

  • dfg/DFGPredictionPropagationPhase.cpp:
  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileCreateRest):

  • dfg/DFGSpeculativeJIT32_64.cpp:

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

  • dfg/DFGSpeculativeJIT64.cpp:

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

  • dfg/DFGStructureRegistrationPhase.cpp:

(JSC::DFG::StructureRegistrationPhase::run):

  • dfg/DFGValidate.cpp:
  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileGetMyArgumentByVal):
(JSC::FTL::DFG::LowerDFGToB3::compileCreateRest):
(JSC::FTL::DFG::LowerDFGToB3::compileForwardVarargs):
(JSC::FTL::DFG::LowerDFGToB3::getArgumentsStart):

  • ftl/FTLOperations.cpp:

(JSC::FTL::operationPopulateObjectInOSR):
(JSC::FTL::operationMaterializeObjectInOSR):

  • jit/SetupVarargsFrame.cpp:

(JSC::emitSetVarargsFrame):

  • runtime/CommonSlowPaths.cpp:

(JSC::SLOW_PATH_DECL):

  • runtime/JSGlobalObject.h:

(JSC::JSGlobalObject::restParameterStructure):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/dfg/DFGArgumentsUtilities.cpp

    r203006 r208208  
    6565        graph, arguments,
    6666        arguments->op() == CreateDirectArguments || arguments->op() == CreateScopedArguments
    67         || arguments->op() == CreateClonedArguments || arguments->op() == PhantomDirectArguments
    68         || arguments->op() == PhantomClonedArguments);
     67        || arguments->op() == CreateClonedArguments || arguments->op() == CreateRest
     68        || arguments->op() == PhantomDirectArguments || arguments->op() == PhantomClonedArguments || arguments->op() == PhantomCreateRest);
    6969   
    7070    InlineCallFrame* inlineCallFrame = arguments->origin.semantic.inlineCallFrame;
     71
     72    unsigned numberOfArgumentsToSkip = 0;
     73    if (arguments->op() == CreateRest || arguments->op() == PhantomCreateRest)
     74        numberOfArgumentsToSkip = arguments->numberOfArgumentsToSkip();
    7175   
    7276    if (inlineCallFrame && !inlineCallFrame->isVarargs()) {
     77        unsigned argumentsSize = inlineCallFrame->arguments.size() - 1;
     78        if (argumentsSize >= numberOfArgumentsToSkip)
     79            argumentsSize -= numberOfArgumentsToSkip;
     80        else
     81            argumentsSize = 0;
    7382        return insertionSet.insertConstant(
    74             nodeIndex, origin, jsNumber(inlineCallFrame->arguments.size() - 1));
     83            nodeIndex, origin, jsNumber(argumentsSize));
    7584    }
    7685   
     
    8594            OpInfo(graph.m_stackAccessData.add(argumentCountRegister, FlushedInt32)));
    8695    }
    87    
    88     return insertionSet.insertNode(
     96
     97    Node* result = insertionSet.insertNode(
    8998        nodeIndex, SpecInt32Only, ArithSub, origin, OpInfo(Arith::Unchecked),
    9099        Edge(argumentCount, Int32Use),
    91100        insertionSet.insertConstantForUse(
    92             nodeIndex, origin, jsNumber(1), Int32Use));
     101            nodeIndex, origin, jsNumber(1 + numberOfArgumentsToSkip), Int32Use));
     102
     103    if (numberOfArgumentsToSkip) {
     104        // The above subtraction may produce a negative number if this number is non-zero. We correct that here.
     105        result = insertionSet.insertNode(
     106            nodeIndex, SpecInt32Only, ArithMax, origin,
     107            Edge(result, Int32Use),
     108            insertionSet.insertConstantForUse(nodeIndex, origin, jsNumber(0), Int32Use));
     109        result->setResult(NodeResultInt32);
     110    }
     111
     112    return result;
    93113}
    94114
Note: See TracChangeset for help on using the changeset viewer.