Ignore:
Timestamp:
Nov 1, 2016, 1:03:03 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/DFGNode.h

    r208224 r208235  
    459459        case PhantomDirectArguments:
    460460        case PhantomClonedArguments:
     461        case PhantomCreateRest:
    461462            // These pretend to be the empty value constant for the benefit of the DFG backend, which
    462463            // otherwise wouldn't take kindly to a node that doesn't compute a value.
     
    472473        ASSERT(hasConstant());
    473474       
    474         if (op() == PhantomDirectArguments || op() == PhantomClonedArguments) {
     475        if (op() == PhantomDirectArguments || op() == PhantomClonedArguments || op() == PhantomCreateRest) {
    475476            // These pretend to be the empty value constant for the benefit of the DFG backend, which
    476477            // otherwise wouldn't take kindly to a node that doesn't compute a value.
     
    17461747        case PhantomNewObject:
    17471748        case PhantomDirectArguments:
     1749        case PhantomCreateRest:
    17481750        case PhantomClonedArguments:
    17491751        case PhantomNewFunction:
     
    23892391    unsigned numberOfArgumentsToSkip()
    23902392    {
    2391         ASSERT(op() == CreateRest || op() == GetRestLength);
     2393        ASSERT(op() == CreateRest || op() == PhantomCreateRest || op() == GetRestLength || op() == GetMyArgumentByVal || op() == GetMyArgumentByValOutOfBounds);
    23922394        return m_opInfo.as<unsigned>();
    23932395    }
Note: See TracChangeset for help on using the changeset viewer.