Ignore:
Timestamp:
Sep 27, 2017, 11:37:41 AM (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):

Source/JavaScriptCore:

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/DFGOperations.cpp

    r222473 r222563  
    870870    NativeCallFrameTracer tracer(vm, exec);
    871871   
    872     array->push(exec, JSValue::decode(encodedValue));
     872    array->pushInline(exec, JSValue::decode(encodedValue));
    873873    return JSValue::encode(jsNumber(array->length()));
    874874}
     
    879879    NativeCallFrameTracer tracer(vm, exec);
    880880   
    881     array->push(exec, JSValue(JSValue::EncodeAsDouble, value));
     881    array->pushInline(exec, JSValue(JSValue::EncodeAsDouble, value));
     882    return JSValue::encode(jsNumber(array->length()));
     883}
     884
     885EncodedJSValue JIT_OPERATION operationArrayPushMultiple(ExecState* exec, JSArray* array, void* buffer, int32_t elementCount)
     886{
     887    VM& vm = exec->vm();
     888    NativeCallFrameTracer tracer(&vm, exec);
     889    auto scope = DECLARE_THROW_SCOPE(vm);
     890
     891    // We assume that multiple JSArray::push calls with ArrayWithInt32/ArrayWithContiguous do not cause JS traps.
     892    // If it can cause any JS interactions, we can call the caller JS function of this function and overwrite the
     893    // content of ScratchBuffer. If the IndexingType is now ArrayWithInt32/ArrayWithContiguous, we can ensure
     894    // that there is no indexed accessors in this object and its prototype chain.
     895    //
     896    // ArrayWithArrayStorage is also OK. It can have indexed accessors. But if you define an indexed accessor, the array's length
     897    // becomes larger than that index. So Array#push never overlaps with this accessor. So accessors are never called unless
     898    // the IndexingType is ArrayWithSlowPutArrayStorage which could have an indexed accessor in a prototype chain.
     899    RELEASE_ASSERT(!shouldUseSlowPut(array->indexingType()));
     900
     901    EncodedJSValue* values = static_cast<EncodedJSValue*>(buffer);
     902    for (int32_t i = 0; i < elementCount; ++i) {
     903        array->pushInline(exec, JSValue::decode(values[i]));
     904        RETURN_IF_EXCEPTION(scope, encodedJSValue());
     905    }
     906    return JSValue::encode(jsNumber(array->length()));
     907}
     908
     909EncodedJSValue JIT_OPERATION operationArrayPushDoubleMultiple(ExecState* exec, JSArray* array, void* buffer, int32_t elementCount)
     910{
     911    VM& vm = exec->vm();
     912    NativeCallFrameTracer tracer(&vm, exec);
     913    auto scope = DECLARE_THROW_SCOPE(vm);
     914
     915    // We assume that multiple JSArray::push calls with ArrayWithDouble do not cause JS traps.
     916    // If it can cause any JS interactions, we can call the caller JS function of this function and overwrite the
     917    // content of ScratchBuffer. If the IndexingType is now ArrayWithDouble, we can ensure
     918    // that there is no indexed accessors in this object and its prototype chain.
     919    ASSERT(array->indexingType() == ArrayWithDouble);
     920
     921    double* values = static_cast<double*>(buffer);
     922    for (int32_t i = 0; i < elementCount; ++i) {
     923        array->pushInline(exec, JSValue(JSValue::EncodeAsDouble, values[i]));
     924        RETURN_IF_EXCEPTION(scope, encodedJSValue());
     925    }
    882926    return JSValue::encode(jsNumber(array->length()));
    883927}
Note: See TracChangeset for help on using the changeset viewer.