Permalink
Browse files

We should have a more concise way of determining when we're varargs c…

…alling a function using rest parameters

https://bugs.webkit.org/show_bug.cgi?id=164258

Reviewed by Yusuke Suzuki.

JSTests:

* microbenchmarks/call-using-spread.js: Added.
(bar):
(foo):
* microbenchmarks/spread-large-array.js: Added.
(foo):
(arrays.push):
* microbenchmarks/spread-small-array.js: Added.
(foo):
* stress/spread-array-iterator-watchpoint-2.js: Added.
(foo):
(arrayIterator.next):
* stress/spread-array-iterator-watchpoint.js: Added.
(foo):
(Array.prototype.Symbol.iterator):
* stress/spread-non-array.js: Added.
(assert):
(foo):
(let.customIterator.Symbol.iterator):
(bar):

Source/JavaScriptCore:

This patch adds two new bytecodes and DFG nodes for the following code patterns:

```
foo(a, b, ...c)
let x = [a, b, ...c];
```

To do this, I've introduced two new bytecode operations (and their
corresponding DFG nodes):

op_spread and op_new_array_with_spread.

op_spread takes a single input and performs the ES6 iteration protocol on it.
It returns the result of doing the spread inside a new class I've
made called JSFixedArray. JSFixedArray is a cell with a single 'size'
field and a buffer of values allocated inline in the cell. Abstracting
the protocol into a single node is good because it will make IR analysis
in the future much simpler. For now, it's also good because it allows
us to create fast paths for array iteration (which is quite common).
This fast path allows us to emit really good code for array iteration
inside the DFG/FTL.

op_new_array_with_spread is a variable argument bytecode that also
has a bit vector associated with it. The bit vector indicates if
any particular argument is to be spread or not. Arguments that
are spread are known to be JSFixedArray because we must emit an
op_spread before op_new_array_with_spread consumes the value.
For example, for this array:
[a, b, ...c, d, ...e]
we will have this bit vector:
[0, 0, 1, 0, 1]

The reason I've chosen this IR is that it will make eliminating
a rest allocation for this type of code much easier:

```
function foo(...args) {
    return bar(a, b, ...args);
}
```

It will be easier to analyze the IR now that the operations
will be described at a high level.

This patch is an ~8% speedup on ES6SampleBench on my MBP.

* CMakeLists.txt:
* DerivedSources.make:
* JavaScriptCore.xcodeproj/project.pbxproj:
* builtins/IteratorHelpers.js: Added.
(performIteration):
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecode/ObjectPropertyConditionSet.cpp:
(JSC::generateConditionForSelfEquivalence):
* bytecode/ObjectPropertyConditionSet.h:
* bytecode/TrackedReferences.cpp:
(JSC::TrackedReferences::check):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::bitVectors):
(JSC::UnlinkedCodeBlock::bitVector):
(JSC::UnlinkedCodeBlock::addBitVector):
(JSC::UnlinkedCodeBlock::shrinkToFit):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitNewArrayWithSpread):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::ArrayNode::emitBytecode):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::addToGraph):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::watchHavingABadTime):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::isWatchingArrayIteratorProtocolWatchpoint):
* dfg/DFGNode.h:
(JSC::DFG::Node::bitVector):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileSpread):
(JSC::DFG::SpeculativeJIT::compileNewArrayWithSpread):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStructureRegistrationPhase.cpp:
(JSC::DFG::StructureRegistrationPhase::run):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSpread):
(JSC::FTL::DFG::LowerDFGToB3::compileSpread):
(JSC::FTL::DFG::LowerDFGToB3::allocateVariableSizedCell):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::emitAllocateVariableSizedCell):
(JSC::AssemblyHelpers::emitAllocateVariableSizedJSObject):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_new_array_with_spread):
(JSC::JIT::emit_op_spread):
* jit/JITOperations.h:
* llint/LLIntData.cpp:
(JSC::LLInt::Data::performAssertions):
* llint/LLIntSlowPaths.cpp:
* llint/LowLevelInterpreter.asm:
* runtime/ArrayIteratorAdaptiveWatchpoint.cpp: Added.
(JSC::ArrayIteratorAdaptiveWatchpoint::ArrayIteratorAdaptiveWatchpoint):
(JSC::ArrayIteratorAdaptiveWatchpoint::handleFire):
* runtime/ArrayIteratorAdaptiveWatchpoint.h: Added.
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
* runtime/IteratorOperations.h:
(JSC::forEachInIterable):
* runtime/JSCInlines.h:
* runtime/JSFixedArray.cpp: Added.
(JSC::JSFixedArray::visitChildren):
* runtime/JSFixedArray.h: Added.
(JSC::JSFixedArray::createStructure):
(JSC::JSFixedArray::createFromArray):
(JSC::JSFixedArray::get):
(JSC::JSFixedArray::buffer):
(JSC::JSFixedArray::size):
(JSC::JSFixedArray::offsetOfSize):
(JSC::JSFixedArray::offsetOfData):
(JSC::JSFixedArray::create):
(JSC::JSFixedArray::JSFixedArray):
(JSC::JSFixedArray::allocationSize):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::JSGlobalObject):
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
(JSC::JSGlobalObject::objectPrototypeIsSane): Deleted.
(JSC::JSGlobalObject::arrayPrototypeChainIsSane): Deleted.
(JSC::JSGlobalObject::stringPrototypeChainIsSane): Deleted.
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::arrayIteratorProtocolWatchpoint):
(JSC::JSGlobalObject::iteratorProtocolFunction):
* runtime/JSGlobalObjectInlines.h: Added.
(JSC::JSGlobalObject::objectPrototypeIsSane):
(JSC::JSGlobalObject::arrayPrototypeChainIsSane):
(JSC::JSGlobalObject::stringPrototypeChainIsSane):
(JSC::JSGlobalObject::isArrayIteratorProtocolFastAndNonObservable):
* runtime/JSType.h:
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@208637 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information...
sbarati@apple.com
sbarati@apple.com committed Nov 12, 2016
1 parent 3bf15b4 commit 99ed47942b1dcf935accb23b355bc8a2e93650c9
Showing with 1,875 additions and 63 deletions.
  1. +27 −0 JSTests/ChangeLog
  2. +14 −0 JSTests/microbenchmarks/call-using-spread.js
  3. +39 −0 JSTests/microbenchmarks/spread-large-array.js
  4. +19 −0 JSTests/microbenchmarks/spread-small-array.js
  5. +22 −0 JSTests/stress/spread-array-iterator-watchpoint-2.js
  6. +25 −0 JSTests/stress/spread-array-iterator-watchpoint.js
  7. +62 −0 JSTests/stress/spread-non-array.js
  8. +3 −0 Source/JavaScriptCore/CMakeLists.txt
  9. +179 −0 Source/JavaScriptCore/ChangeLog
  10. +1 −0 Source/JavaScriptCore/DerivedSources.make
  11. +20 −0 Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
  12. +46 −0 Source/JavaScriptCore/builtins/IteratorHelpers.js
  13. +2 −0 Source/JavaScriptCore/bytecode/BytecodeList.json
  14. +4 −0 Source/JavaScriptCore/bytecode/BytecodeUseDef.h
  15. +24 −0 Source/JavaScriptCore/bytecode/CodeBlock.cpp
  16. +6 −0 Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp
  17. +3 −0 Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h
  18. +1 −1 Source/JavaScriptCore/bytecode/TrackedReferences.cpp
  19. +11 −0 Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
  20. +44 −0 Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
  21. +1 −0 Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
  22. +15 −0 Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
  23. +21 −0 Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
  24. +22 −1 Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
  25. +2 −0 Source/JavaScriptCore/dfg/DFGCapabilities.cpp
  26. +29 −0 Source/JavaScriptCore/dfg/DFGClobberize.h
  27. +2 −0 Source/JavaScriptCore/dfg/DFGDoesGC.cpp
  28. +42 −1 Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
  29. +21 −0 Source/JavaScriptCore/dfg/DFGGraph.h
  30. +6 −0 Source/JavaScriptCore/dfg/DFGNode.h
  31. +2 −0 Source/JavaScriptCore/dfg/DFGNodeType.h
  32. +91 −0 Source/JavaScriptCore/dfg/DFGOperations.cpp
  33. +4 −0 Source/JavaScriptCore/dfg/DFGOperations.h
  34. +5 −0 Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
  35. +2 −0 Source/JavaScriptCore/dfg/DFGSafeToExecute.h
  36. +243 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
  37. +7 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
  38. +10 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
  39. +10 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
  40. +16 −0 Source/JavaScriptCore/dfg/DFGStructureRegistrationPhase.cpp
  41. +3 −0 Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
  42. +2 −0 Source/JavaScriptCore/ftl/FTLCapabilities.cpp
  43. +204 −0 Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
  44. +7 −1 Source/JavaScriptCore/jit/AssemblyHelpers.h
  45. +2 −0 Source/JavaScriptCore/jit/JIT.cpp
  46. +2 −0 Source/JavaScriptCore/jit/JIT.h
  47. +12 −0 Source/JavaScriptCore/jit/JITOpcodes.cpp
  48. +1 −0 Source/JavaScriptCore/jit/JITOperations.h
  49. +15 −15 Source/JavaScriptCore/llint/LLIntData.cpp
  50. +2 −0 Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
  51. +27 −15 Source/JavaScriptCore/llint/LowLevelInterpreter.asm
  52. +45 −0 Source/JavaScriptCore/runtime/ArrayIteratorAdaptiveWatchpoint.cpp
  53. +45 −0 Source/JavaScriptCore/runtime/ArrayIteratorAdaptiveWatchpoint.h
  54. +81 −0 Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
  55. +2 −0 Source/JavaScriptCore/runtime/CommonSlowPaths.h
  56. +7 −7 Source/JavaScriptCore/runtime/IteratorOperations.h
  57. +1 −0 Source/JavaScriptCore/runtime/JSCInlines.h
  58. +43 −0 Source/JavaScriptCore/runtime/JSFixedArray.cpp
  59. +136 −0 Source/JavaScriptCore/runtime/JSFixedArray.h
  60. +49 −21 Source/JavaScriptCore/runtime/JSGlobalObject.cpp
  61. +12 −1 Source/JavaScriptCore/runtime/JSGlobalObject.h
  62. +69 −0 Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h
  63. +2 −0 Source/JavaScriptCore/runtime/JSType.h
  64. +2 −0 Source/JavaScriptCore/runtime/VM.cpp
  65. +1 −0 Source/JavaScriptCore/runtime/VM.h
View
@@ -1,3 +1,30 @@
2016-11-11 Saam Barati <sbarati@apple.com>
We should have a more concise way of determining when we're varargs calling a function using rest parameters
https://bugs.webkit.org/show_bug.cgi?id=164258
Reviewed by Yusuke Suzuki.
* microbenchmarks/call-using-spread.js: Added.
(bar):
(foo):
* microbenchmarks/spread-large-array.js: Added.
(foo):
(arrays.push):
* microbenchmarks/spread-small-array.js: Added.
(foo):
* stress/spread-array-iterator-watchpoint-2.js: Added.
(foo):
(arrayIterator.next):
* stress/spread-array-iterator-watchpoint.js: Added.
(foo):
(Array.prototype.Symbol.iterator):
* stress/spread-non-array.js: Added.
(assert):
(foo):
(let.customIterator.Symbol.iterator):
(bar):
2016-11-11 Keith Miller <keith_miller@apple.com>
Relocate wasm tests and actually add them to the test runner
@@ -0,0 +1,14 @@
function bar(a, b, c, d, e, f) { }
noInline(bar);
function foo(a, b, ...args) {
return bar(a, b, ...args);
}
noInline(foo);
let start = Date.now();
for (let i = 0; i < 500000; i++) {
foo(i, i+1, i+2, i+3, i+4, i+5);
}
const verbose = false;
if (verbose)
print(Date.now() - start);
@@ -0,0 +1,39 @@
function foo(arg) {
return [...arg];
}
noInline(foo);
let arrays = [ ];
const size = 500;
{
let arr = [];
for (let i = 0; i < size; i++) {
arr.push(i);
}
arrays.push(arr);
}
{
let arr = [];
for (let i = 0; i < size; i++) {
arr.push(i + 0.5);
}
arrays.push(arr);
}
{
let arr = [];
for (let i = 0; i < size; i++) {
arr.push({i: i});
}
arrays.push(arr);
}
let start = Date.now();
for (let i = 0; i < 100000; i++) {
let array = arrays[i % arrays.length];
foo(array);
}
const verbose = false;
if (verbose)
print(Date.now() - start);
@@ -0,0 +1,19 @@
function foo(arg) {
return [...arg];
}
noInline(foo);
let arrays = [
[10, 20, 40],
[10.5, 20.5, 40.5],
[20, {}, 8],
];
let start = Date.now();
for (let i = 0; i < 10000000; i++) {
let array = arrays[i % arrays.length];
foo(array);
}
const verbose = false;
if (verbose)
print(Date.now() - start);
@@ -0,0 +1,22 @@
function foo(a) {
return [...a];
}
noInline(foo);
let arr = [];
for (let i = 0; i < 10000; i++) {
if (i % 100 === 0)
arr.push([], i);
foo(arr);
}
let calledIterator = false;
let arrayIterator = [][Symbol.iterator]().__proto__;
arrayIterator.next = function() {
calledIterator = true;
return {done: true};
};
let r = foo(arr);
if (!calledIterator || r.length)
throw new Error("Bad result");
@@ -0,0 +1,25 @@
function foo(a) {
return [...a];
}
noInline(foo);
let arr = [];
for (let i = 0; i < 10000; i++) {
if (i % 100 === 0)
arr.push([], i);
foo(arr);
}
let calledIterator = false;
Array.prototype[Symbol.iterator] = function iterator() {
calledIterator = true;
return {
next() {
return {done: true};
}
};
};
foo(arr);
if (!calledIterator)
throw new Error("Bad result");
@@ -0,0 +1,62 @@
function assert(b) {
if (!b)
throw new Error("Bad assertion.");
}
function foo(m) {
return [...m];
}
noInline(foo);
let map = new Map;
map.set(20, 30);
map.set(40, 50);
let called = 0;
let customIterator = {
[Symbol.iterator]: function() {
called++;
let count = 0;
return {
next() {
called++;
count++;
if (count === 1)
return {done: false, value: [20, 30]};
if (count === 2)
return {done: false, value: [40, 50]};
return {done: true};
}
};
}
};
for (let i = 0; i < 10000; i++) {
for (let o of [customIterator, map]) {
let [[a, b], [c, d]] = foo(o);
assert(a === 20);
assert(b === 30);
assert(c === 40);
assert(d === 50);
}
assert(called === 4);
called = 0;
}
function bar(m) {
return [...m, ...m];
}
noInline(bar);
for (let i = 0; i < 10000; i++) {
for (let o of [customIterator, map]) {
let [[a, b], [c, d], [e, f], [g, h]] = bar(o);
assert(a === 20);
assert(b === 30);
assert(c === 40);
assert(d === 50);
assert(e === 20);
assert(f === 30);
assert(g === 40);
assert(h === 50);
}
assert(called === 8);
called = 0;
}
@@ -650,6 +650,7 @@ set(JavaScriptCore_SOURCES
runtime/ArrayBufferView.cpp
runtime/ArrayConstructor.cpp
runtime/ArrayConventions.cpp
runtime/ArrayIteratorAdaptiveWatchpoint.cpp
runtime/ArrayIteratorPrototype.cpp
runtime/ArrayPrototype.cpp
runtime/AtomicsObject.cpp
@@ -743,6 +744,7 @@ set(JavaScriptCore_SOURCES
runtime/JSDataViewPrototype.cpp
runtime/JSDateMath.cpp
runtime/JSEnvironmentRecord.cpp
runtime/JSFixedArray.cpp
runtime/JSFunction.cpp
runtime/JSGeneratorFunction.cpp
runtime/JSGlobalLexicalEnvironment.cpp
@@ -1350,6 +1352,7 @@ set(JavaScriptCore_BUILTINS_SOURCES
${JAVASCRIPTCORE_DIR}/builtins/GlobalOperations.js
${JAVASCRIPTCORE_DIR}/builtins/InspectorInstrumentationObject.js
${JAVASCRIPTCORE_DIR}/builtins/InternalPromiseConstructor.js
${JAVASCRIPTCORE_DIR}/builtins/IteratorHelpers.js
${JAVASCRIPTCORE_DIR}/builtins/IteratorPrototype.js
${JAVASCRIPTCORE_DIR}/builtins/MapPrototype.js
${JAVASCRIPTCORE_DIR}/builtins/ModuleLoaderPrototype.js
Oops, something went wrong.

0 comments on commit 99ed479

Please sign in to comment.