Ignore:
Timestamp:
Sep 29, 2017, 6:16:52 PM (8 years ago)
Author:
Yusuke Suzuki
Message:

[DFG] Support ArrayPush with multiple args
https://bugs.webkit.org/show_bug.cgi?id=175823

Reviewed by Saam Barati.

JSTests:

  • microbenchmarks/array-push-0.js: Added.

(arrayPush0):

  • microbenchmarks/array-push-1.js: Added.

(arrayPush1):

  • microbenchmarks/array-push-2.js: Added.

(arrayPush2):

  • microbenchmarks/array-push-3.js: Added.

(arrayPush3):

  • stress/array-push-multiple-contiguous.js: Added.

(shouldBe):
(test):

  • stress/array-push-multiple-double-nan.js: Added.

(shouldBe):
(test):

  • stress/array-push-multiple-double.js: Added.

(shouldBe):
(test):

  • stress/array-push-multiple-int32.js: Added.

(shouldBe):
(test):

  • stress/array-push-multiple-many-contiguous.js: Added.

(shouldBe):
(test):

  • stress/array-push-multiple-many-double.js: Added.

(shouldBe):
(test):

  • stress/array-push-multiple-many-int32.js: Added.

(shouldBe):
(test):

  • stress/array-push-multiple-many-storage.js: Added.

(shouldBe):
(test):

  • stress/array-push-multiple-storage.js: Added.

(shouldBe):
(test):

  • stress/array-push-with-force-exit.js: Added.

(target.createBuiltin):

Source/JavaScriptCore:

Reviewed by Saam Barati.

This patch implements ArrayPush(with multiple arguments) in DFG and FTL. Previously, they are not handled
by ArrayPush. Then they go to generic direct call to Array#push and it does in slow path. This patch
extends ArrayPush to push multiple arguments in a bulk push manner.

The problem of ArrayPush is that we need to perform ArrayPush atomically: If OSR exit occurs in the middle
of ArrayPush, we incorrectly push pushed elements twice. Once we start pushing values, we should not exit.
But we do not want to iterate elements twice, once for type checks and once for actually pushing it. It
could move elements between registers and memory back and forth.

This patch achieves the above goal by separating type checks from ArrayPush. When starting ArrayPush, type
checks for elements are already done by separately emitted Check nodes.

We also add JSArray::pushInline for DFG operations just calling JSArray::push. And we also use it in
arrayProtoFuncPush's fast path.

This patch significantly improves performance of push(multiple args).

baseline patched

Microbenchmarks:

array-push-0 461.8455+-28.9995 151.3438+-6.5653 definitely 3.0516x faster
array-push-1 133.8845+-7.0349 ? 136.1775+-5.8327 ? might be 1.0171x slower
array-push-2 675.6555+-13.4645 145.8747+-6.4621 definitely 4.6318x faster
array-push-3 849.5284+-15.2540 253.4421+-9.1249 definitely 3.3520x faster

baseline patched

SixSpeed:

spread-literal.es5 90.3482+-6.6514 24.8123+-2.3304 definitely 3.6413x faster

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::handleIntrinsicCall):

  • dfg/DFGFixupPhase.cpp:

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

  • dfg/DFGNodeType.h:
  • dfg/DFGOperations.cpp:
  • dfg/DFGOperations.h:
  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileArrayPush):

  • dfg/DFGSpeculativeJIT.h:

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

  • dfg/DFGSpeculativeJIT32_64.cpp:

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

  • dfg/DFGSpeculativeJIT64.cpp:

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

  • dfg/DFGStoreBarrierInsertionPhase.cpp:
  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileArrayPush):

  • jit/JITOperations.h:
  • runtime/ArrayPrototype.cpp:

(JSC::arrayProtoFuncPush):

  • runtime/JSArray.cpp:

(JSC::JSArray::push):

  • runtime/JSArray.h:
  • runtime/JSArrayInlines.h:

(JSC::JSArray::pushInline):

File:
1 edited

Legend:

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

    r222658 r222675  
    10091009            // array-related - so if refine() turned this into a "Generic" ArrayPush then
    10101010            // that would break things.
    1011             node->setArrayMode(
    1012                 node->arrayMode().refine(
    1013                     m_graph, node,
    1014                     node->child1()->prediction() & SpecCell,
    1015                     SpecInt32Only,
    1016                     node->child2()->prediction()));
    1017             blessArrayOperation(node->child1(), Edge(), node->child3());
    1018             fixEdge<KnownCellUse>(node->child1());
    1019            
    1020             switch (node->arrayMode().type()) {
    1021             case Array::Int32:
    1022                 fixEdge<Int32Use>(node->child2());
    1023                 break;
    1024             case Array::Double:
    1025                 fixEdge<DoubleRepRealUse>(node->child2());
    1026                 break;
    1027             case Array::Contiguous:
    1028             case Array::ArrayStorage:
    1029                 speculateForBarrier(node->child2());
    1030                 break;
    1031             default:
    1032                 break;
     1011            Edge& storageEdge = m_graph.varArgChild(node, 0);
     1012            Edge& arrayEdge = m_graph.varArgChild(node, 1);
     1013            unsigned elementOffset = 2;
     1014            unsigned elementCount = node->numChildren() - elementOffset;
     1015            for (unsigned i = 0; i < elementCount; ++i) {
     1016                Edge& element = m_graph.varArgChild(node, i + elementOffset);
     1017                node->setArrayMode(
     1018                    node->arrayMode().refine(
     1019                        m_graph, node,
     1020                        arrayEdge->prediction() & SpecCell,
     1021                        SpecInt32Only,
     1022                        element->prediction()));
     1023            }
     1024            blessArrayOperation(arrayEdge, Edge(), storageEdge);
     1025            fixEdge<KnownCellUse>(arrayEdge);
     1026
     1027            // Convert `array.push()` to GetArrayLength.
     1028            if (!elementCount && node->arrayMode().supportsSelfLength()) {
     1029                node->setOpAndDefaultFlags(GetArrayLength);
     1030                node->child1() = arrayEdge;
     1031                node->child2() = storageEdge;
     1032                fixEdge<KnownCellUse>(node->child1());
     1033                break;
     1034            }
     1035
     1036            // We do not want to perform osr exit and retry for ArrayPush. We insert Check with appropriate type,
     1037            // and ArrayPush uses the edge as known typed edge. Therefore, ArrayPush do not need to perform type checks.
     1038            for (unsigned i = 0; i < elementCount; ++i) {
     1039                Edge& element = m_graph.varArgChild(node, i + elementOffset);
     1040                switch (node->arrayMode().type()) {
     1041                case Array::Int32:
     1042                    insertCheck<Int32Use>(element.node());
     1043                    fixEdge<KnownInt32Use>(element);
     1044                    break;
     1045                case Array::Double:
     1046                    insertCheck<DoubleRepRealUse>(element.node());
     1047                    fixEdge<DoubleRepUse>(element);
     1048                    break;
     1049                case Array::Contiguous:
     1050                case Array::ArrayStorage:
     1051                    speculateForBarrier(element);
     1052                    break;
     1053                default:
     1054                    break;
     1055                }
     1056                ASSERT(shouldNotHaveTypeCheck(element.useKind()));
    10331057            }
    10341058            break;
Note: See TracChangeset for help on using the changeset viewer.