Changeset 157267 in webkit


Ignore:
Timestamp:
Oct 10, 2013 4:16:04 PM (11 years ago)
Author:
oliver@apple.com
Message:

Further improve ArrayIterator performance
https://bugs.webkit.org/show_bug.cgi?id=122575

Reviewed by Mark Hahnenberg.

Source/JavaScriptCore:

Add an assembly thunk for ArrayIterator.@@next so that we
can avoid marshalling costs when iterating arrays.

  • jit/SpecializedThunkJIT.h:

(JSC::SpecializedThunkJIT::SpecializedThunkJIT):
(JSC::SpecializedThunkJIT::loadSpecificClassArgument):

  • jit/ThunkGenerators.cpp:

(JSC::arrayIteratorNextThunkGenerator):

  • jit/ThunkGenerators.h:
  • runtime/ArrayIteratorPrototype.cpp:

(JSC::ArrayIteratorPrototype::finishCreation):

  • runtime/Intrinsic.h:
  • runtime/JSArrayIterator.h:

(JSC::JSArrayIterator::offsetOfIterationKind):
(JSC::JSArrayIterator::offsetOfIteratedObject):
(JSC::JSArrayIterator::offsetOfNextIndex):

  • runtime/JSCJSValue.h:

(JSC::JSValue::offsetOfPayload):

  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::reset):

  • runtime/JSGlobalObject.h:

(JSC::JSGlobalObject::iteratorResultStructureOffset):

  • runtime/VM.cpp:

(JSC::thunkGeneratorForIntrinsic):

LayoutTests:

Add a few new tests to make sure the new asm thunk correctly
handles non-arrays.

  • js/array-iterators-expected.txt:
  • js/script-tests/array-iterators.js:
Location:
trunk
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r157265 r157267  
     12013-10-09  Oliver Hunt  <oliver@apple.com>
     2
     3        Further improve ArrayIterator performance
     4        https://bugs.webkit.org/show_bug.cgi?id=122575
     5
     6        Reviewed by Mark Hahnenberg.
     7
     8        Add a few new tests to make sure the new asm thunk correctly
     9        handles non-arrays.
     10
     11        * js/array-iterators-expected.txt:
     12        * js/script-tests/array-iterators.js:
     13
    1142013-10-10  Andy Estes  <aestes@apple.com>
    215
  • trunk/LayoutTests/js/array-iterators-expected.txt

    r157150 r157267  
    5656PASS key is i
    5757PASS testArray.length is 9
     58PASS i is 0
     59PASS value is o[key]
     60PASS key is 0
     61PASS value is 1
     62PASS value is o[key]
     63PASS key is 1
     64PASS value is 2
     65PASS value is o[key]
     66PASS key is 2
     67PASS value is 3
     68PASS value is o[key]
     69PASS key is 3
     70PASS value is 4
     71PASS value is o[key]
     72PASS key is 4
     73PASS value is 5
     74PASS value is o[key]
     75PASS key is 5
     76PASS value is 6
     77PASS o.length is 6
     78PASS value is 1.5
     79PASS value is 2.5
     80PASS value is 3.5
     81PASS value is 4.5
     82PASS value is 5.5
     83PASS value is 6.5
     84PASS testArray.length is 6
     85PASS value is true
     86PASS value is true
     87PASS value is true
     88PASS value is true
     89PASS value is true
     90PASS value is true
     91PASS testArray.length is 6
     92PASS isNaN(value) is true
     93PASS isNaN(value) is true
     94PASS isNaN(value) is true
     95PASS isNaN(value) is true
     96PASS isNaN(value) is true
     97PASS isNaN(value) is true
     98PASS testArray.length is 6
     99PASS value is undefined.
     100PASS value is undefined.
     101PASS value is undefined.
     102PASS value is undefined.
     103PASS value is undefined.
     104PASS value is undefined.
     105PASS value is undefined.
     106PASS value is undefined.
     107PASS testArray.length is 8
     108PASS key is 0
     109PASS key is 1
     110PASS key is 2
     111PASS key is 3
     112PASS key is 4
     113PASS key is 5
     114PASS key is 6
     115PASS key is 7
     116PASS testArray.length is 8
    58117PASS successfullyParsed is true
    59118
  • trunk/LayoutTests/js/script-tests/array-iterators.js

    r157150 r157267  
    5151shouldBe("testArray.length", String(i))
    5252
     53var o = {};
     54for (var i =0; i < 6; i++)
     55    o[i]=i+1
     56
     57
     58var entries = Array.prototype.entries.call(o);
     59var i = 0;
     60for (var [key, value] of entries) {
     61    fail();
     62}
     63shouldBe("i", "0")
     64
     65o.length=6;
     66
     67var entries = Array.prototype.entries.call(o);
     68var i = 0;
     69for (var [key, value] of entries) {
     70    shouldBe("value", "o[key]")
     71    shouldBe("key", String(i))
     72    i++
     73    shouldBe("value", String(i))
     74}
     75shouldBe("o.length", String(i))
     76
     77
     78var testArray = [1.5,2.5,3.5,4.5,5.5,6.5]
     79var i = 0;
     80for (var value of testArray) {
     81    shouldBe("value", String(i + 1.5))
     82    i++;
     83}
     84
     85shouldBe("testArray.length", String(i))
     86
     87
     88var testArray = [true,true,true,true,true,true]
     89var i = 0;
     90for (var value of testArray) {
     91    shouldBe("value", "true")
     92    i++;
     93}
     94
     95shouldBe("testArray.length", String(i))
     96
     97
     98var testArray = [NaN,NaN,NaN,NaN,NaN,NaN]
     99var i = 0;
     100for (var value of testArray) {
     101    shouldBeTrue("isNaN(value)")
     102    i++;
     103}
     104
     105shouldBe("testArray.length", String(i))
     106
     107
     108
     109var testArray = [undefined,,undefined,,undefined,undefined]
     110testArray.length = 8;
     111var i = 0;
     112for (var value of testArray) {
     113    shouldBeUndefined("value")
     114    i++;
     115}
     116
     117shouldBe("testArray.length", String(i))
     118var i = 0;
     119for (var key of testArray.keys()) {
     120    shouldBe("key", String(i))
     121    i++;
     122}
     123
     124shouldBe("testArray.length", String(i))
  • trunk/Source/JavaScriptCore/ChangeLog

    r157266 r157267  
     12013-10-09  Oliver Hunt  <oliver@apple.com>
     2
     3        Further improve ArrayIterator performance
     4        https://bugs.webkit.org/show_bug.cgi?id=122575
     5
     6        Reviewed by Mark Hahnenberg.
     7
     8        Add an assembly thunk for ArrayIterator.@@next so that we
     9        can avoid marshalling costs when iterating arrays.
     10
     11        * jit/SpecializedThunkJIT.h:
     12        (JSC::SpecializedThunkJIT::SpecializedThunkJIT):
     13        (JSC::SpecializedThunkJIT::loadSpecificClassArgument):
     14        * jit/ThunkGenerators.cpp:
     15        (JSC::arrayIteratorNextThunkGenerator):
     16        * jit/ThunkGenerators.h:
     17        * runtime/ArrayIteratorPrototype.cpp:
     18        (JSC::ArrayIteratorPrototype::finishCreation):
     19        * runtime/Intrinsic.h:
     20        * runtime/JSArrayIterator.h:
     21        (JSC::JSArrayIterator::offsetOfIterationKind):
     22        (JSC::JSArrayIterator::offsetOfIteratedObject):
     23        (JSC::JSArrayIterator::offsetOfNextIndex):
     24        * runtime/JSCJSValue.h:
     25        (JSC::JSValue::offsetOfPayload):
     26        * runtime/JSGlobalObject.cpp:
     27        (JSC::JSGlobalObject::reset):
     28        * runtime/JSGlobalObject.h:
     29        (JSC::JSGlobalObject::iteratorResultStructureOffset):
     30        * runtime/VM.cpp:
     31        (JSC::thunkGeneratorForIntrinsic):
     32
    1332013-10-10  Michael Saboff  <msaboff@apple.com>
    234
  • trunk/Source/JavaScriptCore/jit/SpecializedThunkJIT.h

    r156184 r157267  
    3131#include "Executable.h"
    3232#include "JSInterfaceJIT.h"
     33#include "JSStack.h"
    3334#include "LinkBuffer.h"
    3435
     
    4546        }
    4647       
     48        explicit SpecializedThunkJIT(VM* vm)
     49            : JSInterfaceJIT(vm)
     50        {
     51        }
     52       
    4753        void loadDoubleArgument(int argument, FPRegisterID dst, RegisterID scratch)
    4854        {
     
    6167            loadCellArgument(argument, dst);
    6268            m_failures.append(branchPtr(NotEqual, Address(dst, JSCell::structureOffset()), TrustedImmPtr(vm.stringStructure.get())));
     69        }
     70       
     71        void loadArgumentWithSpecificClass(const ClassInfo* classInfo, int argument, RegisterID dst, RegisterID scratch)
     72        {
     73            loadCellArgument(argument, dst);
     74            loadPtr(Address(dst, JSCell::structureOffset()), scratch);
     75            appendFailure(branchPtr(NotEqual, Address(scratch, Structure::classInfoOffset()), TrustedImmPtr(classInfo)));
    6376        }
    6477       
     
    8093            m_failures.append(failure);
    8194        }
    82 
     95#if USE(JSVALUE64)
    8396        void returnJSValue(RegisterID src)
    8497        {
     
    88101            ret();
    89102        }
     103#else
     104        void returnJSValue(RegisterID payload, RegisterID tag)
     105        {
     106            ASSERT_UNUSED(payload, payload == regT0);
     107            ASSERT_UNUSED(tag, tag == regT1);
     108            loadPtr(payloadFor(JSStack::CallerFrame, callFrameRegister), callFrameRegister);
     109            ret();
     110        }
     111#endif
    90112       
    91113        void returnDouble(FPRegisterID src)
  • trunk/Source/JavaScriptCore/jit/ThunkGenerators.cpp

    r157164 r157267  
    2929#include "CodeBlock.h"
    3030#include "JITOperations.h"
     31#include "JSArray.h"
     32#include "JSArrayIterator.h"
    3133#include "JSStack.h"
    3234#include "Operations.h"
     
    987989}
    988990
     991MacroAssemblerCodeRef arrayIteratorNextThunkGenerator(VM* vm)
     992{
     993    typedef SpecializedThunkJIT::TrustedImm32 TrustedImm32;
     994    typedef SpecializedThunkJIT::TrustedImmPtr TrustedImmPtr;
     995    typedef SpecializedThunkJIT::Address Address;
     996    typedef SpecializedThunkJIT::BaseIndex BaseIndex;
     997    typedef SpecializedThunkJIT::Jump Jump;
     998   
     999    SpecializedThunkJIT jit(vm);
     1000    // Make sure we're being called on an array iterator, and load m_iteratedObject, and m_nextIndex into regT0 and regT1 respectively
     1001    jit.loadArgumentWithSpecificClass(JSArrayIterator::info(), SpecializedThunkJIT::ThisArgument, SpecializedThunkJIT::regT4, SpecializedThunkJIT::regT1);
     1002
     1003    // Early exit if we don't have a thunk for this form of iteration
     1004    jit.appendFailure(jit.branch32(SpecializedThunkJIT::AboveOrEqual, Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfIterationKind()), TrustedImm32(ArrayIterateKeyValue)));
     1005   
     1006    jit.loadPtr(Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfIteratedObject()), SpecializedThunkJIT::regT0);
     1007   
     1008    jit.load32(Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()), SpecializedThunkJIT::regT1);
     1009   
     1010    // Pull out the butterfly from iteratedObject
     1011    jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSCell::structureOffset()), SpecializedThunkJIT::regT2);
     1012   
     1013    jit.load8(Address(SpecializedThunkJIT::regT2, Structure::indexingTypeOffset()), SpecializedThunkJIT::regT3);
     1014    jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
     1015   
     1016    jit.and32(TrustedImm32(IndexingShapeMask), SpecializedThunkJIT::regT3);
     1017   
     1018    Jump notDone = jit.branch32(SpecializedThunkJIT::Below, SpecializedThunkJIT::regT1, Address(SpecializedThunkJIT::regT2, Butterfly::offsetOfPublicLength()));
     1019    // Return the termination signal to indicate that we've finished
     1020    jit.move(TrustedImmPtr(vm->iterationTerminator.get()), SpecializedThunkJIT::regT0);
     1021    jit.returnJSCell(SpecializedThunkJIT::regT0);
     1022   
     1023    notDone.link(&jit);
     1024   
     1025   
     1026    Jump notKey = jit.branch32(SpecializedThunkJIT::NotEqual, Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfIterationKind()), TrustedImm32(ArrayIterateKey));
     1027    // If we're doing key iteration we just need to increment m_nextIndex and return the current value
     1028    jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
     1029    jit.returnInt32(SpecializedThunkJIT::regT1);
     1030   
     1031    notKey.link(&jit);
     1032   
     1033   
     1034    // Okay, now we're returning a value so make sure we're inside the vector size
     1035    jit.appendFailure(jit.branch32(SpecializedThunkJIT::AboveOrEqual, SpecializedThunkJIT::regT1, Address(SpecializedThunkJIT::regT2, Butterfly::offsetOfVectorLength())));
     1036   
     1037    // So now we perform inline loads for int32, value/undecided, and double storage
     1038    Jump undecidedStorage = jit.branch32(SpecializedThunkJIT::Equal, SpecializedThunkJIT::regT3, TrustedImm32(UndecidedShape));
     1039    Jump notContiguousStorage = jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(ContiguousShape));
     1040   
     1041    undecidedStorage.link(&jit);
     1042   
     1043    jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
     1044   
     1045#if USE(JSVALUE64)
     1046    jit.load64(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight), SpecializedThunkJIT::regT0);
     1047    Jump notHole = jit.branchTest64(SpecializedThunkJIT::NonZero, SpecializedThunkJIT::regT0);
     1048    jit.move(JSInterfaceJIT::TrustedImm64(ValueUndefined), JSInterfaceJIT::regT0);
     1049    notHole.link(&jit);
     1050    jit.addPtr(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
     1051    jit.returnJSValue(SpecializedThunkJIT::regT0);
     1052#else
     1053    jit.load32(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight, JSValue::offsetOfTag()), SpecializedThunkJIT::regT3);
     1054    Jump notHole = jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(JSValue::EmptyValueTag));
     1055    jit.move(JSInterfaceJIT::TrustedImm32(JSValue::UndefinedTag), JSInterfaceJIT::regT1);
     1056    jit.move(JSInterfaceJIT::TrustedImm32(0), JSInterfaceJIT::regT0);
     1057    jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
     1058    jit.returnJSValue(SpecializedThunkJIT::regT0, JSInterfaceJIT::regT1);
     1059    notHole.link(&jit);
     1060    jit.load32(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight, JSValue::offsetOfPayload()), SpecializedThunkJIT::regT0);
     1061    jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
     1062    jit.move(SpecializedThunkJIT::regT3, SpecializedThunkJIT::regT1);
     1063    jit.returnJSValue(SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT1);
     1064#endif
     1065    notContiguousStorage.link(&jit);
     1066   
     1067    Jump notInt32Storage = jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(Int32Shape));
     1068    jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
     1069    jit.load32(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight, JSValue::offsetOfPayload()), SpecializedThunkJIT::regT0);
     1070    jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
     1071    jit.returnInt32(SpecializedThunkJIT::regT0);
     1072    notInt32Storage.link(&jit);
     1073   
     1074    jit.appendFailure(jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(DoubleShape)));
     1075    jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
     1076    jit.loadDouble(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight), SpecializedThunkJIT::fpRegT0);
     1077    jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
     1078    jit.returnDouble(SpecializedThunkJIT::fpRegT0);
     1079   
     1080    return jit.finalize(vm->jitStubs->ctiNativeCall(vm), "array-iterator-next");
     1081}
     1082   
    9891083}
    9901084
  • trunk/Source/JavaScriptCore/jit/ThunkGenerators.h

    r157164 r157267  
    5959MacroAssemblerCodeRef powThunkGenerator(VM*);
    6060MacroAssemblerCodeRef imulThunkGenerator(VM*);
     61MacroAssemblerCodeRef arrayIteratorNextThunkGenerator(VM*);
    6162
    6263}
  • trunk/Source/JavaScriptCore/runtime/ArrayIteratorPrototype.cpp

    r157150 r157267  
    4646    vm.prototypeMap.addPrototype(this);
    4747
    48     JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorNextPrivateName, arrayIteratorPrototypeNext, DontEnum, 0);
     48    JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->iteratorNextPrivateName, arrayIteratorPrototypeNext, DontEnum, 0, ArrayIteratorNextIntrinsic);
     49
    4950    JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorPrivateName, arrayIteratorPrototypeIterate, DontEnum, 0);
    5051}
  • trunk/Source/JavaScriptCore/runtime/Intrinsic.h

    r149159 r157267  
    4949    RegExpTestIntrinsic,
    5050    StringPrototypeValueOfIntrinsic,
    51     IMulIntrinsic
     51    IMulIntrinsic,
     52    ArrayIteratorNextIntrinsic
    5253};
    5354
  • trunk/Source/JavaScriptCore/runtime/JSArrayIterator.h

    r156910 r157267  
    3131namespace JSC {
    3232
    33 enum ArrayIterationKind {
     33enum ArrayIterationKind : uint32_t {
    3434    ArrayIterateKey,
    3535    ArrayIterateValue,
     
    6464    size_t nextIndex() const { return m_nextIndex; }
    6565    void setNextIndex(size_t nextIndex) { m_nextIndex = nextIndex; }
    66     void finish() { m_nextIndex = std::numeric_limits<size_t>::max(); }
     66    void finish() { m_nextIndex = std::numeric_limits<uint32_t>::max(); }
    6767   
    6868    using JSNonFinalObject::arrayStorageOrNull;
     69    static ptrdiff_t offsetOfIterationKind() { return OBJECT_OFFSETOF(JSArrayIterator, m_iterationKind); }
     70    static ptrdiff_t offsetOfIteratedObject() { return OBJECT_OFFSETOF(JSArrayIterator, m_iteratedObject); }
     71    static ptrdiff_t offsetOfNextIndex() { return OBJECT_OFFSETOF(JSArrayIterator, m_nextIndex); }
    6972
    7073private:
     
    8386    ArrayIterationKind m_iterationKind;
    8487    WriteBarrier<JSObject> m_iteratedObject;
    85     size_t m_nextIndex;
     88    uint32_t m_nextIndex;
    8689};
    8790
  • trunk/Source/JavaScriptCore/runtime/JSCJSValue.h

    r156184 r157267  
    279279    static const unsigned numberOfInt52Bits = 52;
    280280    static const unsigned int52ShiftAmount = 12;
     281   
     282    static ptrdiff_t offsetOfPayload() { return OBJECT_OFFSETOF(JSValue, u.asBits.payload); }
     283    static ptrdiff_t offsetOfTag() { return OBJECT_OFFSETOF(JSValue, u.asBits.tag); }
    281284
    282285private:
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h

    r156791 r157267  
    407407    Structure* stringObjectStructure() const { return m_stringObjectStructure.get(); }
    408408    Structure* iteratorResultStructure() const { return m_iteratorResultStructure.get(); }
     409    static ptrdiff_t iteratorResultStructureOffset() { return OBJECT_OFFSETOF(JSGlobalObject, m_iteratorResultStructure); }
    409410
    410411#if ENABLE(PROMISES)
  • trunk/Source/JavaScriptCore/runtime/VM.cpp

    r157264 r157267  
    426426    case IMulIntrinsic:
    427427        return imulThunkGenerator;
     428    case ArrayIteratorNextIntrinsic:
     429        return arrayIteratorNextThunkGenerator;
    428430    default:
    429431        return 0;
Note: See TracChangeset for help on using the changeset viewer.