Ignore:
Timestamp:
Nov 11, 2016, 12:33:34 AM (9 years ago)
Author:
[email protected]
Message:

We should have a more concise way of determining when we're varargs calling a function using rest parameters
https://bugs.webkit.org/show_bug.cgi?id=164258

Reviewed by Yusuke Suzuki.

JSTests:

  • microbenchmarks/call-using-spread.js: Added.

(bar):
(foo):

  • microbenchmarks/spread-large-array.js: Added.

(foo):
(arrays.push):

  • microbenchmarks/spread-small-array.js: Added.

(foo):

  • stress/spread-array-iterator-watchpoint-2.js: Added.

(foo):
(arrayIterator.next):

  • stress/spread-array-iterator-watchpoint.js: Added.

(foo):
(Array.prototype.Symbol.iterator):

  • stress/spread-non-array.js: Added.

(assert):
(foo):
(let.customIterator.Symbol.iterator):
(bar):

Source/JavaScriptCore:

This patch adds two new bytecodes and DFG nodes for the following code patterns:

`
foo(a, b, ...c)
let x = [a, b, ...c];
`

To do this, I've introduced two new bytecode operations (and their
corresponding DFG nodes):

op_spread and op_new_array_with_spread.

op_spread takes a single input and performs the ES6 iteration protocol on it.
It returns the result of doing the spread inside a new class I've
made called JSFixedArray. JSFixedArray is a cell with a single 'size'
field and a buffer of values allocated inline in the cell. Abstracting
the protocol into a single node is good because it will make IR analysis
in the future much simpler. For now, it's also good because it allows
us to create fast paths for array iteration (which is quite common).
This fast path allows us to emit really good code for array iteration
inside the DFG/FTL.

op_new_array_with_spread is a variable argument bytecode that also
has a bit vector associated with it. The bit vector indicates if
any particular argument is to be spread or not. Arguments that
are spread are known to be JSFixedArray because we must emit an
op_spread before op_new_array_with_spread consumes the value.
For example, for this array:
[a, b, ...c, d, ...e]
we will have this bit vector:
[0, 0, 1, 0, 1]

The reason I've chosen this IR is that it will make eliminating
a rest allocation for this type of code much easier:

`
function foo(...args) {

return bar(a, b, ...args);

}
`

It will be easier to analyze the IR now that the operations
will be described at a high level.

This patch is an ~8% speedup on ES6SampleBench on my MBP.

  • CMakeLists.txt:
  • DerivedSources.make:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • builtins/IteratorHelpers.js: Added.

(performIteration):

  • bytecode/BytecodeList.json:
  • bytecode/BytecodeUseDef.h:

(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dumpBytecode):

  • bytecode/ObjectPropertyConditionSet.cpp:

(JSC::generateConditionForSelfEquivalence):

  • bytecode/ObjectPropertyConditionSet.h:
  • bytecode/TrackedReferences.cpp:

(JSC::TrackedReferences::check):

  • bytecode/UnlinkedCodeBlock.h:

(JSC::UnlinkedCodeBlock::bitVectors):
(JSC::UnlinkedCodeBlock::bitVector):
(JSC::UnlinkedCodeBlock::addBitVector):
(JSC::UnlinkedCodeBlock::shrinkToFit):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitNewArrayWithSpread):

  • bytecompiler/BytecodeGenerator.h:
  • bytecompiler/NodesCodegen.cpp:

(JSC::ArrayNode::emitBytecode):

  • dfg/DFGAbstractInterpreterInlines.h:

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

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::addToGraph):
(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGCapabilities.cpp:

(JSC::DFG::capabilityLevel):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

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

  • dfg/DFGGraph.h:

(JSC::DFG::Graph::isWatchingArrayIteratorProtocolWatchpoint):

  • dfg/DFGNode.h:

(JSC::DFG::Node::bitVector):

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

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileSpread):
(JSC::DFG::SpeculativeJIT::compileNewArrayWithSpread):

  • dfg/DFGSpeculativeJIT.h:

(JSC::DFG::SpeculativeJIT::callOperation):

  • dfg/DFGSpeculativeJIT32_64.cpp:

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

  • dfg/DFGSpeculativeJIT64.cpp:

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

  • dfg/DFGStructureRegistrationPhase.cpp:

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

  • ftl/FTLAbstractHeapRepository.h:
  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSpread):
(JSC::FTL::DFG::LowerDFGToB3::compileSpread):
(JSC::FTL::DFG::LowerDFGToB3::allocateVariableSizedCell):

  • jit/AssemblyHelpers.h:

(JSC::AssemblyHelpers::emitAllocateVariableSizedCell):
(JSC::AssemblyHelpers::emitAllocateVariableSizedJSObject):

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):

  • jit/JIT.h:
  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_new_array_with_spread):
(JSC::JIT::emit_op_spread):

  • jit/JITOperations.h:
  • llint/LLIntData.cpp:

(JSC::LLInt::Data::performAssertions):

  • llint/LLIntSlowPaths.cpp:
  • llint/LowLevelInterpreter.asm:
  • runtime/ArrayIteratorAdaptiveWatchpoint.cpp: Added.

(JSC::ArrayIteratorAdaptiveWatchpoint::ArrayIteratorAdaptiveWatchpoint):
(JSC::ArrayIteratorAdaptiveWatchpoint::handleFire):

  • runtime/ArrayIteratorAdaptiveWatchpoint.h: Added.
  • runtime/CommonSlowPaths.cpp:

(JSC::SLOW_PATH_DECL):

  • runtime/CommonSlowPaths.h:
  • runtime/IteratorOperations.h:

(JSC::forEachInIterable):

  • runtime/JSCInlines.h:
  • runtime/JSFixedArray.cpp: Added.

(JSC::JSFixedArray::visitChildren):

  • runtime/JSFixedArray.h: Added.

(JSC::JSFixedArray::createStructure):
(JSC::JSFixedArray::createFromArray):
(JSC::JSFixedArray::get):
(JSC::JSFixedArray::buffer):
(JSC::JSFixedArray::size):
(JSC::JSFixedArray::offsetOfSize):
(JSC::JSFixedArray::offsetOfData):
(JSC::JSFixedArray::create):
(JSC::JSFixedArray::JSFixedArray):
(JSC::JSFixedArray::allocationSize):

  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::JSGlobalObject):
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
(JSC::JSGlobalObject::objectPrototypeIsSane): Deleted.
(JSC::JSGlobalObject::arrayPrototypeChainIsSane): Deleted.
(JSC::JSGlobalObject::stringPrototypeChainIsSane): Deleted.

  • runtime/JSGlobalObject.h:

(JSC::JSGlobalObject::arrayIteratorProtocolWatchpoint):
(JSC::JSGlobalObject::iteratorProtocolFunction):

  • runtime/JSGlobalObjectInlines.h: Added.

(JSC::JSGlobalObject::objectPrototypeIsSane):
(JSC::JSGlobalObject::arrayPrototypeChainIsSane):
(JSC::JSGlobalObject::stringPrototypeChainIsSane):
(JSC::JSGlobalObject::isArrayIteratorProtocolFastAndNonObservable):

  • runtime/JSType.h:
  • runtime/VM.cpp:

(JSC::VM::VM):

  • runtime/VM.h:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/dfg/DFGGraph.h

    r208560 r208584  
    666666        return watchpoints().isWatched(globalObject->havingABadTimeWatchpoint());
    667667    }
     668
     669    bool isWatchingArrayIteratorProtocolWatchpoint(Node* node)
     670    {
     671        JSGlobalObject* globalObject = globalObjectFor(node->origin.semantic);
     672        InlineWatchpointSet& set = globalObject->arrayIteratorProtocolWatchpoint();
     673        if (watchpoints().isWatched(set))
     674            return true;
     675
     676        if (set.isStillValid()) {
     677            // Since the global object owns this watchpoint, we make ourselves have a weak pointer to it.
     678            // If the global object got deallocated, it wouldn't fire the watchpoint. It's unlikely the
     679            // global object would get deallocated without this code ever getting thrown away, however,
     680            // it's more sound logically to depend on the global object lifetime weakly.
     681            freeze(globalObject);
     682            watchpoints().addLazily(set);
     683            return true;
     684        }
     685
     686        return false;
     687    }
    668688   
    669689    Profiler::Compilation* compilation() { return m_plan.compilation.get(); }
     
    904924    Bag<LazyJSValue> m_lazyJSValues;
    905925    Bag<CallDOMGetterData> m_callDOMGetterData;
     926    Bag<BitVector> m_bitVectors;
    906927    Vector<InlineVariableData, 4> m_inlineVariableData;
    907928    HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>> m_bytecodeLiveness;
Note: See TracChangeset for help on using the changeset viewer.