Commit 585c400c authored by oliver@apple.com's avatar oliver@apple.com

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:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@157267 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent c8dd3f72
2013-10-09 Oliver Hunt <oliver@apple.com>
Further improve ArrayIterator performance
https://bugs.webkit.org/show_bug.cgi?id=122575
Reviewed by Mark Hahnenberg.
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:
2013-10-10 Andy Estes <aestes@apple.com>
Add SPI for telling WebKit to prefer pictograph glyphs over monochrome ones
......@@ -55,6 +55,65 @@ PASS key is i
PASS value is testArray[key]
PASS key is i
PASS testArray.length is 9
PASS i is 0
PASS value is o[key]
PASS key is 0
PASS value is 1
PASS value is o[key]
PASS key is 1
PASS value is 2
PASS value is o[key]
PASS key is 2
PASS value is 3
PASS value is o[key]
PASS key is 3
PASS value is 4
PASS value is o[key]
PASS key is 4
PASS value is 5
PASS value is o[key]
PASS key is 5
PASS value is 6
PASS o.length is 6
PASS value is 1.5
PASS value is 2.5
PASS value is 3.5
PASS value is 4.5
PASS value is 5.5
PASS value is 6.5
PASS testArray.length is 6
PASS value is true
PASS value is true
PASS value is true
PASS value is true
PASS value is true
PASS value is true
PASS testArray.length is 6
PASS isNaN(value) is true
PASS isNaN(value) is true
PASS isNaN(value) is true
PASS isNaN(value) is true
PASS isNaN(value) is true
PASS isNaN(value) is true
PASS testArray.length is 6
PASS value is undefined.
PASS value is undefined.
PASS value is undefined.
PASS value is undefined.
PASS value is undefined.
PASS value is undefined.
PASS value is undefined.
PASS value is undefined.
PASS testArray.length is 8
PASS key is 0
PASS key is 1
PASS key is 2
PASS key is 3
PASS key is 4
PASS key is 5
PASS key is 6
PASS key is 7
PASS testArray.length is 8
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -50,3 +50,75 @@ for (var [key, value] of entries) {
}
shouldBe("testArray.length", String(i))
var o = {};
for (var i =0; i < 6; i++)
o[i]=i+1
var entries = Array.prototype.entries.call(o);
var i = 0;
for (var [key, value] of entries) {
fail();
}
shouldBe("i", "0")
o.length=6;
var entries = Array.prototype.entries.call(o);
var i = 0;
for (var [key, value] of entries) {
shouldBe("value", "o[key]")
shouldBe("key", String(i))
i++
shouldBe("value", String(i))
}
shouldBe("o.length", String(i))
var testArray = [1.5,2.5,3.5,4.5,5.5,6.5]
var i = 0;
for (var value of testArray) {
shouldBe("value", String(i + 1.5))
i++;
}
shouldBe("testArray.length", String(i))
var testArray = [true,true,true,true,true,true]
var i = 0;
for (var value of testArray) {
shouldBe("value", "true")
i++;
}
shouldBe("testArray.length", String(i))
var testArray = [NaN,NaN,NaN,NaN,NaN,NaN]
var i = 0;
for (var value of testArray) {
shouldBeTrue("isNaN(value)")
i++;
}
shouldBe("testArray.length", String(i))
var testArray = [undefined,,undefined,,undefined,undefined]
testArray.length = 8;
var i = 0;
for (var value of testArray) {
shouldBeUndefined("value")
i++;
}
shouldBe("testArray.length", String(i))
var i = 0;
for (var key of testArray.keys()) {
shouldBe("key", String(i))
i++;
}
shouldBe("testArray.length", String(i))
2013-10-09 Oliver Hunt <oliver@apple.com>
Further improve ArrayIterator performance
https://bugs.webkit.org/show_bug.cgi?id=122575
Reviewed by Mark Hahnenberg.
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):
2013-10-10 Michael Saboff <msaboff@apple.com>
transition cti_op_* methods returning int to JIT operations.
......@@ -30,6 +30,7 @@
#include "Executable.h"
#include "JSInterfaceJIT.h"
#include "JSStack.h"
#include "LinkBuffer.h"
namespace JSC {
......@@ -44,6 +45,11 @@ namespace JSC {
m_failures.append(branch32(NotEqual, payloadFor(JSStack::ArgumentCount), TrustedImm32(expectedArgCount + 1)));
}
explicit SpecializedThunkJIT(VM* vm)
: JSInterfaceJIT(vm)
{
}
void loadDoubleArgument(int argument, FPRegisterID dst, RegisterID scratch)
{
unsigned src = CallFrame::argumentOffset(argument);
......@@ -62,6 +68,13 @@ namespace JSC {
m_failures.append(branchPtr(NotEqual, Address(dst, JSCell::structureOffset()), TrustedImmPtr(vm.stringStructure.get())));
}
void loadArgumentWithSpecificClass(const ClassInfo* classInfo, int argument, RegisterID dst, RegisterID scratch)
{
loadCellArgument(argument, dst);
loadPtr(Address(dst, JSCell::structureOffset()), scratch);
appendFailure(branchPtr(NotEqual, Address(scratch, Structure::classInfoOffset()), TrustedImmPtr(classInfo)));
}
void loadInt32Argument(int argument, RegisterID dst, Jump& failTarget)
{
unsigned src = CallFrame::argumentOffset(argument);
......@@ -79,7 +92,7 @@ namespace JSC {
{
m_failures.append(failure);
}
#if USE(JSVALUE64)
void returnJSValue(RegisterID src)
{
if (src != regT0)
......@@ -87,6 +100,15 @@ namespace JSC {
loadPtr(payloadFor(JSStack::CallerFrame, callFrameRegister), callFrameRegister);
ret();
}
#else
void returnJSValue(RegisterID payload, RegisterID tag)
{
ASSERT_UNUSED(payload, payload == regT0);
ASSERT_UNUSED(tag, tag == regT1);
loadPtr(payloadFor(JSStack::CallerFrame, callFrameRegister), callFrameRegister);
ret();
}
#endif
void returnDouble(FPRegisterID src)
{
......
......@@ -28,6 +28,8 @@
#include "CodeBlock.h"
#include "JITOperations.h"
#include "JSArray.h"
#include "JSArrayIterator.h"
#include "JSStack.h"
#include "Operations.h"
#include "SpecializedThunkJIT.h"
......@@ -986,6 +988,98 @@ MacroAssemblerCodeRef imulThunkGenerator(VM* vm)
return jit.finalize(vm->jitStubs->ctiNativeCall(vm), "imul");
}
MacroAssemblerCodeRef arrayIteratorNextThunkGenerator(VM* vm)
{
typedef SpecializedThunkJIT::TrustedImm32 TrustedImm32;
typedef SpecializedThunkJIT::TrustedImmPtr TrustedImmPtr;
typedef SpecializedThunkJIT::Address Address;
typedef SpecializedThunkJIT::BaseIndex BaseIndex;
typedef SpecializedThunkJIT::Jump Jump;
SpecializedThunkJIT jit(vm);
// Make sure we're being called on an array iterator, and load m_iteratedObject, and m_nextIndex into regT0 and regT1 respectively
jit.loadArgumentWithSpecificClass(JSArrayIterator::info(), SpecializedThunkJIT::ThisArgument, SpecializedThunkJIT::regT4, SpecializedThunkJIT::regT1);
// Early exit if we don't have a thunk for this form of iteration
jit.appendFailure(jit.branch32(SpecializedThunkJIT::AboveOrEqual, Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfIterationKind()), TrustedImm32(ArrayIterateKeyValue)));
jit.loadPtr(Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfIteratedObject()), SpecializedThunkJIT::regT0);
jit.load32(Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()), SpecializedThunkJIT::regT1);
// Pull out the butterfly from iteratedObject
jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSCell::structureOffset()), SpecializedThunkJIT::regT2);
jit.load8(Address(SpecializedThunkJIT::regT2, Structure::indexingTypeOffset()), SpecializedThunkJIT::regT3);
jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
jit.and32(TrustedImm32(IndexingShapeMask), SpecializedThunkJIT::regT3);
Jump notDone = jit.branch32(SpecializedThunkJIT::Below, SpecializedThunkJIT::regT1, Address(SpecializedThunkJIT::regT2, Butterfly::offsetOfPublicLength()));
// Return the termination signal to indicate that we've finished
jit.move(TrustedImmPtr(vm->iterationTerminator.get()), SpecializedThunkJIT::regT0);
jit.returnJSCell(SpecializedThunkJIT::regT0);
notDone.link(&jit);
Jump notKey = jit.branch32(SpecializedThunkJIT::NotEqual, Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfIterationKind()), TrustedImm32(ArrayIterateKey));
// If we're doing key iteration we just need to increment m_nextIndex and return the current value
jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
jit.returnInt32(SpecializedThunkJIT::regT1);
notKey.link(&jit);
// Okay, now we're returning a value so make sure we're inside the vector size
jit.appendFailure(jit.branch32(SpecializedThunkJIT::AboveOrEqual, SpecializedThunkJIT::regT1, Address(SpecializedThunkJIT::regT2, Butterfly::offsetOfVectorLength())));
// So now we perform inline loads for int32, value/undecided, and double storage
Jump undecidedStorage = jit.branch32(SpecializedThunkJIT::Equal, SpecializedThunkJIT::regT3, TrustedImm32(UndecidedShape));
Jump notContiguousStorage = jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(ContiguousShape));
undecidedStorage.link(&jit);
jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
#if USE(JSVALUE64)
jit.load64(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight), SpecializedThunkJIT::regT0);
Jump notHole = jit.branchTest64(SpecializedThunkJIT::NonZero, SpecializedThunkJIT::regT0);
jit.move(JSInterfaceJIT::TrustedImm64(ValueUndefined), JSInterfaceJIT::regT0);
notHole.link(&jit);
jit.addPtr(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
jit.returnJSValue(SpecializedThunkJIT::regT0);
#else
jit.load32(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight, JSValue::offsetOfTag()), SpecializedThunkJIT::regT3);
Jump notHole = jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(JSValue::EmptyValueTag));
jit.move(JSInterfaceJIT::TrustedImm32(JSValue::UndefinedTag), JSInterfaceJIT::regT1);
jit.move(JSInterfaceJIT::TrustedImm32(0), JSInterfaceJIT::regT0);
jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
jit.returnJSValue(SpecializedThunkJIT::regT0, JSInterfaceJIT::regT1);
notHole.link(&jit);
jit.load32(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight, JSValue::offsetOfPayload()), SpecializedThunkJIT::regT0);
jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
jit.move(SpecializedThunkJIT::regT3, SpecializedThunkJIT::regT1);
jit.returnJSValue(SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT1);
#endif
notContiguousStorage.link(&jit);
Jump notInt32Storage = jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(Int32Shape));
jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
jit.load32(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight, JSValue::offsetOfPayload()), SpecializedThunkJIT::regT0);
jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
jit.returnInt32(SpecializedThunkJIT::regT0);
notInt32Storage.link(&jit);
jit.appendFailure(jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(DoubleShape)));
jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
jit.loadDouble(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight), SpecializedThunkJIT::fpRegT0);
jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
jit.returnDouble(SpecializedThunkJIT::fpRegT0);
return jit.finalize(vm->jitStubs->ctiNativeCall(vm), "array-iterator-next");
}
}
#endif // ENABLE(JIT)
......@@ -58,6 +58,7 @@ MacroAssemblerCodeRef roundThunkGenerator(VM*);
MacroAssemblerCodeRef sqrtThunkGenerator(VM*);
MacroAssemblerCodeRef powThunkGenerator(VM*);
MacroAssemblerCodeRef imulThunkGenerator(VM*);
MacroAssemblerCodeRef arrayIteratorNextThunkGenerator(VM*);
}
#endif // ENABLE(JIT)
......
......@@ -45,7 +45,8 @@ void ArrayIteratorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject
ASSERT(inherits(info()));
vm.prototypeMap.addPrototype(this);
JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorNextPrivateName, arrayIteratorPrototypeNext, DontEnum, 0);
JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->iteratorNextPrivateName, arrayIteratorPrototypeNext, DontEnum, 0, ArrayIteratorNextIntrinsic);
JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorPrivateName, arrayIteratorPrototypeIterate, DontEnum, 0);
}
......
......@@ -48,7 +48,8 @@ enum Intrinsic {
RegExpExecIntrinsic,
RegExpTestIntrinsic,
StringPrototypeValueOfIntrinsic,
IMulIntrinsic
IMulIntrinsic,
ArrayIteratorNextIntrinsic
};
} // namespace JSC
......
......@@ -30,7 +30,7 @@
namespace JSC {
enum ArrayIterationKind {
enum ArrayIterationKind : uint32_t {
ArrayIterateKey,
ArrayIterateValue,
ArrayIterateKeyValue,
......@@ -63,9 +63,12 @@ public:
JSObject* iteratedObject() const { return m_iteratedObject.get(); }
size_t nextIndex() const { return m_nextIndex; }
void setNextIndex(size_t nextIndex) { m_nextIndex = nextIndex; }
void finish() { m_nextIndex = std::numeric_limits<size_t>::max(); }
void finish() { m_nextIndex = std::numeric_limits<uint32_t>::max(); }
using JSNonFinalObject::arrayStorageOrNull;
static ptrdiff_t offsetOfIterationKind() { return OBJECT_OFFSETOF(JSArrayIterator, m_iterationKind); }
static ptrdiff_t offsetOfIteratedObject() { return OBJECT_OFFSETOF(JSArrayIterator, m_iteratedObject); }
static ptrdiff_t offsetOfNextIndex() { return OBJECT_OFFSETOF(JSArrayIterator, m_nextIndex); }
private:
......@@ -82,7 +85,7 @@ private:
ArrayIterationKind m_iterationKind;
WriteBarrier<JSObject> m_iteratedObject;
size_t m_nextIndex;
uint32_t m_nextIndex;
};
}
......
......@@ -278,6 +278,9 @@ public:
// converted to Int52s and back again.
static const unsigned numberOfInt52Bits = 52;
static const unsigned int52ShiftAmount = 12;
static ptrdiff_t offsetOfPayload() { return OBJECT_OFFSETOF(JSValue, u.asBits.payload); }
static ptrdiff_t offsetOfTag() { return OBJECT_OFFSETOF(JSValue, u.asBits.tag); }
private:
template <class T> JSValue(WriteBarrierBase<T>);
......
......@@ -406,6 +406,7 @@ public:
Structure* setStructure() const { return m_setStructure.get(); }
Structure* stringObjectStructure() const { return m_stringObjectStructure.get(); }
Structure* iteratorResultStructure() const { return m_iteratorResultStructure.get(); }
static ptrdiff_t iteratorResultStructureOffset() { return OBJECT_OFFSETOF(JSGlobalObject, m_iteratorResultStructure); }
#if ENABLE(PROMISES)
Structure* promiseStructure() const { return m_promiseStructure.get(); }
......
......@@ -425,6 +425,8 @@ static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic)
return logThunkGenerator;
case IMulIntrinsic:
return imulThunkGenerator;
case ArrayIteratorNextIntrinsic:
return arrayIteratorNextThunkGenerator;
default:
return 0;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment