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/runtime/JSArray.cpp

    r222473 r222563  
    4141namespace JSC {
    4242
    43 static const char* const LengthExceededTheMaximumArrayLengthError = "Length exceeded the maximum array length";
     43const char* const LengthExceededTheMaximumArrayLengthError = "Length exceeded the maximum array length";
    4444
    4545STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray);
     
    726726//  - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
    727727//  - pushing to an array of length 2^32-1 stores the property, but throws a range error.
    728 void JSArray::push(ExecState* exec, JSValue value)
    729 {
    730     VM& vm = exec->vm();
    731     auto scope = DECLARE_THROW_SCOPE(vm);
    732 
    733     Butterfly* butterfly = m_butterfly.getMayBeNull();
    734    
    735     switch (indexingType()) {
    736     case ArrayClass: {
    737         createInitialUndecided(vm, 0);
    738         FALLTHROUGH;
    739     }
    740        
    741     case ArrayWithUndecided: {
    742         convertUndecidedForValue(vm, value);
    743         scope.release();
    744         push(exec, value);
    745         return;
    746     }
    747        
    748     case ArrayWithInt32: {
    749         if (!value.isInt32()) {
    750             convertInt32ForValue(vm, value);
    751             scope.release();
    752             push(exec, value);
    753             return;
    754         }
    755 
    756         unsigned length = butterfly->publicLength();
    757         ASSERT(length <= butterfly->vectorLength());
    758         if (length < butterfly->vectorLength()) {
    759             butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value);
    760             butterfly->setPublicLength(length + 1);
    761             return;
    762         }
    763        
    764         if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
    765             methodTable(vm)->putByIndex(this, exec, length, value, true);
    766             if (!scope.exception())
    767                 throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
    768             return;
    769         }
    770 
    771         scope.release();
    772         putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value);
    773         return;
    774     }
    775 
    776     case ArrayWithContiguous: {
    777         unsigned length = butterfly->publicLength();
    778         ASSERT(length <= butterfly->vectorLength());
    779         if (length < butterfly->vectorLength()) {
    780             butterfly->contiguous()[length].set(vm, this, value);
    781             butterfly->setPublicLength(length + 1);
    782             return;
    783         }
    784        
    785         if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
    786             methodTable(vm)->putByIndex(this, exec, length, value, true);
    787             if (!scope.exception())
    788                 throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
    789             return;
    790         }
    791 
    792         scope.release();
    793         putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value);
    794         return;
    795     }
    796        
    797     case ArrayWithDouble: {
    798         if (!value.isNumber()) {
    799             convertDoubleToContiguous(vm);
    800             scope.release();
    801             push(exec, value);
    802             return;
    803         }
    804         double valueAsDouble = value.asNumber();
    805         if (valueAsDouble != valueAsDouble) {
    806             convertDoubleToContiguous(vm);
    807             scope.release();
    808             push(exec, value);
    809             return;
    810         }
    811 
    812         unsigned length = butterfly->publicLength();
    813         ASSERT(length <= butterfly->vectorLength());
    814         if (length < butterfly->vectorLength()) {
    815             butterfly->contiguousDouble()[length] = valueAsDouble;
    816             butterfly->setPublicLength(length + 1);
    817             return;
    818         }
    819        
    820         if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
    821             methodTable(vm)->putByIndex(this, exec, length, value, true);
    822             if (!scope.exception())
    823                 throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
    824             return;
    825         }
    826 
    827         scope.release();
    828         putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value);
    829         return;
    830     }
    831        
    832     case ArrayWithSlowPutArrayStorage: {
    833         unsigned oldLength = length();
    834         bool putResult = false;
    835         if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true, putResult)) {
    836             if (!scope.exception() && oldLength < 0xFFFFFFFFu) {
    837                 scope.release();
    838                 setLength(exec, oldLength + 1, true);
    839             }
    840             return;
    841         }
    842         FALLTHROUGH;
    843     }
    844        
    845     case ArrayWithArrayStorage: {
    846         ArrayStorage* storage = butterfly->arrayStorage();
    847 
    848         // Fast case - push within vector, always update m_length & m_numValuesInVector.
    849         unsigned length = storage->length();
    850         if (length < storage->vectorLength()) {
    851             storage->m_vector[length].set(vm, this, value);
    852             storage->setLength(length + 1);
    853             ++storage->m_numValuesInVector;
    854             return;
    855         }
    856 
    857         // Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error.
    858         if (UNLIKELY(storage->length() > MAX_ARRAY_INDEX)) {
    859             methodTable(vm)->putByIndex(this, exec, storage->length(), value, true);
    860             // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
    861             if (!scope.exception())
    862                 throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
    863             return;
    864         }
    865 
    866         // Handled the same as putIndex.
    867         scope.release();
    868         putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage);
    869         return;
    870     }
    871        
    872     default:
    873         RELEASE_ASSERT_NOT_REACHED();
    874     }
     728NEVER_INLINE void JSArray::push(ExecState* exec, JSValue value)
     729{
     730    pushInline(exec, value);
    875731}
    876732
Note: See TracChangeset for help on using the changeset viewer.