[JSC] Avoid cloned arguments allocation in ArrayPrototype methods
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Nov 2016 06:34:05 +0000 (06:34 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Nov 2016 06:34:05 +0000 (06:34 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164502

Reviewed by Saam Barati.

JSTests:

* stress/argument-intrinsic-basic.js: Added.
(shouldBe):
(builtin.createBuiltin):
* stress/argument-intrinsic-inlining-with-result-escape.js: Added.
(shouldBe):
(builtin.createBuiltin):
(escape):
* stress/argument-intrinsic-nested-inlining.js: Added.
(shouldBe):
(builtin.createBuiltin):
(builtinCaller1):
(builtinCaller2):
(escape):
* stress/argument-intrinsic-not-convert-to-get-argument.js: Added.
(shouldBe):
(builtin.createBuiltin):
* stress/argument-intrinsic-with-stack-write.js: Added.
(shouldBe):
(builtin.createBuiltin):

Source/JavaScriptCore:

In many builtin functions, we use `arguments` to just get optional parameters.
While FTL argument elimination can drop `arguments` allocations, it leaves
the allocations in LLInt, Baseline, and DFG. And we found that DFG compiled
Array#map is heavily used in ES6SampleBench/Basic. And it always creates
a meaningless ClonedArguments.

Using ES6 default parameter here is not a solution. It increases the number
of parameters of the CodeBlock (not `function.length`). And the optional
parameters in Array.prototype.xxx methods are not typically passed. For
example, we typically do not pass `thisArg` to `Array.prototype.map` function.
In this case, the arity check frequently fails. It requires the additional C
call to fixup arguments and it becomes pure overhead.

To solve this problem, this patch introduces a new bytecode intrinsic @argument().
This offers the way to retrieve the argument value without increasing the
arity of the function. And if the argument is not passed (out of bounds), it
just returns `undefined`. The semantics of this intrinsic is the same to the C++
ExecState::argument(). This operation does not require `arguments` object. And we
can drop the `argument` references even in lower 3 tiers.

We implement op_get_argument for this intrinsic. And later this will be converted
to DFG GetArgument node. All the tiers handles this feature.

This patch improves ES6SampleBench/Basic 13.8% in steady state. And in summary,
it improves 4.5%.

In the future, we can improve the implementation of the default parameters.
Currently, the default parameter always increases the arity of the function. So
if you do not pass the argument, the arity check fails. But since it is the default
parameter, it is likely that we don't pass the argument. Using op_get_argument to
implement the default parameter can decrease the case in which the arity check
frequently fails. And it can change the builtin implementation to use the ES6
default parameters instead of using the special @argument() intrinsic in the future.
And at that case, the user code also receives the benefit.

ES6SampleBench/Basic.
    Baseline:
        Running... Basic ( 1  to go)
        firstIteration:     39.38 ms +- 4.48 ms
        averageWorstCase:   20.79 ms +- 0.96 ms
        steadyState:        1959.22 ms +- 65.55 ms

    Patched:
        Running... Basic ( 1  to go)
        firstIteration:     37.85 ms +- 4.09 ms
        averageWorstCase:   18.60 ms +- 0.76 ms
        steadyState:        1721.89 ms +- 57.58 ms

All summary.
    Baseline:
        summary:            164.34 ms +- 5.01 ms
    Patched:
        summary:            157.26 ms +- 5.96 ms

* builtins/ArrayConstructor.js:
* builtins/ArrayPrototype.js:
(reduce):
(reduceRight):
(every):
(forEach):
(filter):
(map):
(some):
(fill):
(find):
(findIndex):
(includes):
(copyWithin):
* builtins/DatePrototype.js:
(toLocaleString):
(toLocaleDateString):
(toLocaleTimeString):
* builtins/MapPrototype.js:
(forEach):
* builtins/NumberPrototype.js:
(toLocaleString):
* builtins/SetPrototype.js:
(forEach):
* builtins/StringPrototype.js:
(padStart):
(padEnd):
(localeCompare):
* builtins/TypedArrayConstructor.js:
* builtins/TypedArrayPrototype.js:
(every):
(fill):
(find):
(findIndex):
(forEach):
(some):
(reduce):
(reduceRight):
(map):
(filter):
* bytecode/BytecodeIntrinsicRegistry.h:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::finishCreation):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitGetArgument):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::BytecodeIntrinsicNode::emit_intrinsic_argument):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(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):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::hasArgumentIndex):
(JSC::DFG::Node::argumentIndex):
* dfg/DFGNodeType.h:
* dfg/DFGPreciseLocalClobberize.h:
(JSC::DFG::PreciseLocalClobberizeAdaptor::readTop):
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileGetArgument):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileGetArgument):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_get_argument):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_get_argument):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@208524 268f45cc-cd09-0410-ab3c-d52691b4dbfc

50 files changed:
JSTests/ChangeLog
JSTests/stress/argument-intrinsic-basic.js [new file with mode: 0644]
JSTests/stress/argument-intrinsic-inlining-use-caller-arg.js [new file with mode: 0644]
JSTests/stress/argument-intrinsic-inlining-with-result-escape.js [new file with mode: 0644]
JSTests/stress/argument-intrinsic-inlining-with-vararg-with-enough-arguments.js [new file with mode: 0644]
JSTests/stress/argument-intrinsic-inlining-with-vararg.js [new file with mode: 0644]
JSTests/stress/argument-intrinsic-nested-inlining.js [new file with mode: 0644]
JSTests/stress/argument-intrinsic-not-convert-to-get-argument.js [new file with mode: 0644]
JSTests/stress/argument-intrinsic-with-stack-write.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/ArrayConstructor.js
Source/JavaScriptCore/builtins/ArrayPrototype.js
Source/JavaScriptCore/builtins/DatePrototype.js
Source/JavaScriptCore/builtins/MapPrototype.js
Source/JavaScriptCore/builtins/NumberPrototype.js
Source/JavaScriptCore/builtins/SetPrototype.js
Source/JavaScriptCore/builtins/StringPrototype.js
Source/JavaScriptCore/builtins/TypedArrayConstructor.js
Source/JavaScriptCore/builtins/TypedArrayPrototype.js
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm

index 77a1cc3..b2a19b2 100644 (file)
@@ -1,3 +1,30 @@
+2016-11-08  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Avoid cloned arguments allocation in ArrayPrototype methods
+        https://bugs.webkit.org/show_bug.cgi?id=164502
+
+        Reviewed by Saam Barati.
+
+        * stress/argument-intrinsic-basic.js: Added.
+        (shouldBe):
+        (builtin.createBuiltin):
+        * stress/argument-intrinsic-inlining-with-result-escape.js: Added.
+        (shouldBe):
+        (builtin.createBuiltin):
+        (escape):
+        * stress/argument-intrinsic-nested-inlining.js: Added.
+        (shouldBe):
+        (builtin.createBuiltin):
+        (builtinCaller1):
+        (builtinCaller2):
+        (escape):
+        * stress/argument-intrinsic-not-convert-to-get-argument.js: Added.
+        (shouldBe):
+        (builtin.createBuiltin):
+        * stress/argument-intrinsic-with-stack-write.js: Added.
+        (shouldBe):
+        (builtin.createBuiltin):
+
 2016-11-09  Saam Barati  <sbarati@apple.com>
 
         Math.min()/Math.max() with no arguments is lowered incorrectly in the BytecodeParser
 2016-11-09  Saam Barati  <sbarati@apple.com>
 
         Math.min()/Math.max() with no arguments is lowered incorrectly in the BytecodeParser
diff --git a/JSTests/stress/argument-intrinsic-basic.js b/JSTests/stress/argument-intrinsic-basic.js
new file mode 100644 (file)
index 0000000..c0a18b6
--- /dev/null
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var builtin = createBuiltin(`(function (a) {
+    return @argument(1);
+})`);
+noInline(builtin);
+
+(function () {
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(builtin(), undefined);
+        shouldBe(builtin(1), undefined);
+        shouldBe(builtin(1, 2), 2);
+        shouldBe(builtin(1, 2, 3), 2);
+    }
+}());
diff --git a/JSTests/stress/argument-intrinsic-inlining-use-caller-arg.js b/JSTests/stress/argument-intrinsic-inlining-use-caller-arg.js
new file mode 100644 (file)
index 0000000..f431b3a
--- /dev/null
@@ -0,0 +1,31 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test()
+{
+    return 42;
+}
+noInline(test);
+
+var builtin = createBuiltin(`(function (a) {
+    return @argument(2);
+})`);
+
+function inlining(a, b, c)
+{
+    return builtin(1, 2, test(), 4, 5, 6, 7);
+}
+noInline(inlining);
+
+function escape(value)
+{
+    return value;
+}
+noInline(escape);
+
+(function () {
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(escape(inlining(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), 42);
+}());
diff --git a/JSTests/stress/argument-intrinsic-inlining-with-result-escape.js b/JSTests/stress/argument-intrinsic-inlining-with-result-escape.js
new file mode 100644 (file)
index 0000000..07f4ab7
--- /dev/null
@@ -0,0 +1,21 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var builtin = createBuiltin(`(function (a) {
+    return @argument(0);
+})`);
+
+function escape(value)
+{
+    return value;
+}
+noInline(escape);
+
+(function () {
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(escape(builtin()), undefined);
+        shouldBe(escape(builtin(42)), 42);
+    }
+}());
diff --git a/JSTests/stress/argument-intrinsic-inlining-with-vararg-with-enough-arguments.js b/JSTests/stress/argument-intrinsic-inlining-with-vararg-with-enough-arguments.js
new file mode 100644 (file)
index 0000000..95f6249
--- /dev/null
@@ -0,0 +1,25 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var builtin = createBuiltin(`(function (a) {
+    return @argument(5);
+})`);
+
+function inlining(a, b, c)
+{
+    return builtin.call(this, ...[1, 2, 3, 4, 5, 6, 7]);
+}
+noInline(inlining);
+
+function escape(value)
+{
+    return value;
+}
+noInline(escape);
+
+(function () {
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(escape(inlining(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), 6);
+}());
diff --git a/JSTests/stress/argument-intrinsic-inlining-with-vararg.js b/JSTests/stress/argument-intrinsic-inlining-with-vararg.js
new file mode 100644 (file)
index 0000000..1c90d99
--- /dev/null
@@ -0,0 +1,25 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var builtin = createBuiltin(`(function (a) {
+    return @argument(5);
+})`);
+
+function inlining(a, b, c)
+{
+    return builtin.call(this, ...[1, 2, 3, 4, 5, 6, 7]);
+}
+noInline(inlining);
+
+function escape(value)
+{
+    return value;
+}
+noInline(escape);
+
+(function () {
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(escape(inlining(0)), 6);
+}());
diff --git a/JSTests/stress/argument-intrinsic-nested-inlining.js b/JSTests/stress/argument-intrinsic-nested-inlining.js
new file mode 100644 (file)
index 0000000..1c86563
--- /dev/null
@@ -0,0 +1,31 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var builtin = createBuiltin(`(function (a) {
+    return @argument(0);
+})`);
+
+function builtinCaller1()
+{
+    return builtin();
+}
+
+function builtinCaller2()
+{
+    return builtin(42);
+}
+
+function escape(value)
+{
+    return value;
+}
+noInline(escape);
+
+(function () {
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(escape(builtinCaller1()), undefined);
+        shouldBe(escape(builtinCaller2()), 42);
+    }
+}());
diff --git a/JSTests/stress/argument-intrinsic-not-convert-to-get-argument.js b/JSTests/stress/argument-intrinsic-not-convert-to-get-argument.js
new file mode 100644 (file)
index 0000000..63c0007
--- /dev/null
@@ -0,0 +1,15 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var builtin = createBuiltin(`(function (a) {
+    return @argument(0);
+})`);
+
+(function () {
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(builtin(), undefined);
+        shouldBe(builtin(42), 42);
+    }
+}());
diff --git a/JSTests/stress/argument-intrinsic-with-stack-write.js b/JSTests/stress/argument-intrinsic-with-stack-write.js
new file mode 100644 (file)
index 0000000..01140ad
--- /dev/null
@@ -0,0 +1,19 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var builtin = createBuiltin(`(function (a) {
+    a = 42;
+    return @argument(0);
+})`);
+noInline(builtin);
+
+(function () {
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(builtin(), undefined);
+        shouldBe(builtin(1), 42);
+        shouldBe(builtin(1, 2), 42);
+        shouldBe(builtin(1, 2, 3), 42);
+    }
+}());
index d01deb7..3b4dc2e 100644 (file)
@@ -1,3 +1,161 @@
+2016-11-08  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Avoid cloned arguments allocation in ArrayPrototype methods
+        https://bugs.webkit.org/show_bug.cgi?id=164502
+
+        Reviewed by Saam Barati.
+
+        In many builtin functions, we use `arguments` to just get optional parameters.
+        While FTL argument elimination can drop `arguments` allocations, it leaves
+        the allocations in LLInt, Baseline, and DFG. And we found that DFG compiled
+        Array#map is heavily used in ES6SampleBench/Basic. And it always creates
+        a meaningless ClonedArguments.
+
+        Using ES6 default parameter here is not a solution. It increases the number
+        of parameters of the CodeBlock (not `function.length`). And the optional
+        parameters in Array.prototype.xxx methods are not typically passed. For
+        example, we typically do not pass `thisArg` to `Array.prototype.map` function.
+        In this case, the arity check frequently fails. It requires the additional C
+        call to fixup arguments and it becomes pure overhead.
+
+        To solve this problem, this patch introduces a new bytecode intrinsic @argument().
+        This offers the way to retrieve the argument value without increasing the
+        arity of the function. And if the argument is not passed (out of bounds), it
+        just returns `undefined`. The semantics of this intrinsic is the same to the C++
+        ExecState::argument(). This operation does not require `arguments` object. And we
+        can drop the `argument` references even in lower 3 tiers.
+
+        We implement op_get_argument for this intrinsic. And later this will be converted
+        to DFG GetArgument node. All the tiers handles this feature.
+
+        This patch improves ES6SampleBench/Basic 13.8% in steady state. And in summary,
+        it improves 4.5%.
+
+        In the future, we can improve the implementation of the default parameters.
+        Currently, the default parameter always increases the arity of the function. So
+        if you do not pass the argument, the arity check fails. But since it is the default
+        parameter, it is likely that we don't pass the argument. Using op_get_argument to
+        implement the default parameter can decrease the case in which the arity check
+        frequently fails. And it can change the builtin implementation to use the ES6
+        default parameters instead of using the special @argument() intrinsic in the future.
+        And at that case, the user code also receives the benefit.
+
+        ES6SampleBench/Basic.
+            Baseline:
+                Running... Basic ( 1  to go)
+                firstIteration:     39.38 ms +- 4.48 ms
+                averageWorstCase:   20.79 ms +- 0.96 ms
+                steadyState:        1959.22 ms +- 65.55 ms
+
+            Patched:
+                Running... Basic ( 1  to go)
+                firstIteration:     37.85 ms +- 4.09 ms
+                averageWorstCase:   18.60 ms +- 0.76 ms
+                steadyState:        1721.89 ms +- 57.58 ms
+
+        All summary.
+            Baseline:
+                summary:            164.34 ms +- 5.01 ms
+            Patched:
+                summary:            157.26 ms +- 5.96 ms
+
+        * builtins/ArrayConstructor.js:
+        * builtins/ArrayPrototype.js:
+        (reduce):
+        (reduceRight):
+        (every):
+        (forEach):
+        (filter):
+        (map):
+        (some):
+        (fill):
+        (find):
+        (findIndex):
+        (includes):
+        (copyWithin):
+        * builtins/DatePrototype.js:
+        (toLocaleString):
+        (toLocaleDateString):
+        (toLocaleTimeString):
+        * builtins/MapPrototype.js:
+        (forEach):
+        * builtins/NumberPrototype.js:
+        (toLocaleString):
+        * builtins/SetPrototype.js:
+        (forEach):
+        * builtins/StringPrototype.js:
+        (padStart):
+        (padEnd):
+        (localeCompare):
+        * builtins/TypedArrayConstructor.js:
+        * builtins/TypedArrayPrototype.js:
+        (every):
+        (fill):
+        (find):
+        (findIndex):
+        (forEach):
+        (some):
+        (reduce):
+        (reduceRight):
+        (map):
+        (filter):
+        * bytecode/BytecodeIntrinsicRegistry.h:
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dumpBytecode):
+        (JSC::CodeBlock::finishCreation):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitGetArgument):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::BytecodeIntrinsicNode::emit_intrinsic_argument):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (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):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        (JSC::DFG::Node::hasArgumentIndex):
+        (JSC::DFG::Node::argumentIndex):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPreciseLocalClobberize.h:
+        (JSC::DFG::PreciseLocalClobberizeAdaptor::readTop):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileGetArgument):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetArgument):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        * jit/JIT.h:
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_get_argument):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_get_argument):
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+
 2016-11-08  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: DebuggerManager.Event.Resumed introduces test flakiness
 2016-11-08  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: DebuggerManager.Event.Resumed introduces test flakiness
index d159eb2..73add1a 100644 (file)
@@ -41,7 +41,7 @@ function from(items /*, mapFn, thisArg */)
 
     var thisObj = this;
 
 
     var thisObj = this;
 
-    var mapFn = arguments.length > 1 ? arguments[1] : @undefined;
+    var mapFn = @argument(1);
 
     var thisArg;
 
 
     var thisArg;
 
@@ -49,8 +49,7 @@ function from(items /*, mapFn, thisArg */)
         if (typeof mapFn !== "function")
             @throwTypeError("Array.from requires that the second argument, when provided, be a function");
 
         if (typeof mapFn !== "function")
             @throwTypeError("Array.from requires that the second argument, when provided, be a function");
 
-        if (arguments.length > 2)
-            thisArg = arguments[2];
+        thisArg = @argument(2);
     }
 
     if (items == null)
     }
 
     if (items == null)
index 2b5b2cd..89aa66f 100644 (file)
@@ -78,12 +78,13 @@ function reduce(callback /*, initialValue */)
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.reduce callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.reduce callback must be a function");
 
-    if (length === 0 && arguments.length < 2)
+    var argumentCount = @argumentCount();
+    if (length === 0 && argumentCount < 2)
         @throwTypeError("reduce of empty array with no initial value");
 
     var accumulator, k = 0;
         @throwTypeError("reduce of empty array with no initial value");
 
     var accumulator, k = 0;
-    if (arguments.length > 1)
-        accumulator = arguments[1];
+    if (argumentCount > 1)
+        accumulator = @argument(1);
     else {
         while (k < length && !(k in array))
             k += 1;
     else {
         while (k < length && !(k in array))
             k += 1;
@@ -113,12 +114,13 @@ function reduceRight(callback /*, initialValue */)
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.reduceRight callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.reduceRight callback must be a function");
 
-    if (length === 0 && arguments.length < 2)
+    var argumentCount = @argumentCount();
+    if (length === 0 && argumentCount < 2)
         @throwTypeError("reduceRight of empty array with no initial value");
 
     var accumulator, k = length - 1;
         @throwTypeError("reduceRight of empty array with no initial value");
 
     var accumulator, k = length - 1;
-    if (arguments.length > 1)
-        accumulator = arguments[1];
+    if (argumentCount > 1)
+        accumulator = @argument(1);
     else {
         while (k >= 0 && !(k in array))
             k -= 1;
     else {
         while (k >= 0 && !(k in array))
             k -= 1;
@@ -148,7 +150,7 @@ function every(callback /*, thisArg */)
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.every callback must be a function");
     
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.every callback must be a function");
     
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
     
     for (var i = 0; i < length; i++) {
         if (!(i in array))
     
     for (var i = 0; i < length; i++) {
         if (!(i in array))
@@ -173,7 +175,7 @@ function forEach(callback /*, thisArg */)
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.forEach callback must be a function");
     
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.forEach callback must be a function");
     
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
     
     for (var i = 0; i < length; i++) {
         if (i in array)
     
     for (var i = 0; i < length; i++) {
         if (i in array)
@@ -194,7 +196,7 @@ function filter(callback /*, thisArg */)
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.filter callback must be a function");
     
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.filter callback must be a function");
     
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
 
     // Do 9.4.2.3 ArraySpeciesCreate
     var result;
 
     // Do 9.4.2.3 ArraySpeciesCreate
     var result;
@@ -243,7 +245,7 @@ function map(callback /*, thisArg */)
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.map callback must be a function");
     
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.map callback must be a function");
     
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
 
     // Do 9.4.2.3 ArraySpeciesCreate
     var result;
 
     // Do 9.4.2.3 ArraySpeciesCreate
     var result;
@@ -289,7 +291,7 @@ function some(callback /*, thisArg */)
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.some callback must be a function");
     
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.some callback must be a function");
     
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
     for (var i = 0; i < length; i++) {
         if (!(i in array))
             continue;
     for (var i = 0; i < length; i++) {
         if (!(i in array))
             continue;
@@ -309,9 +311,7 @@ function fill(value /* [, start [, end]] */)
     var array = @Object(this);
     var length = @toLength(array.length);
 
     var array = @Object(this);
     var length = @toLength(array.length);
 
-    var relativeStart = 0;
-    if (arguments.length > 1 && arguments[1] !== @undefined)
-        relativeStart = arguments[1] | 0;
+    var relativeStart = @toInteger(@argument(1));
     var k = 0;
     if (relativeStart < 0) {
         k = length + relativeStart;
     var k = 0;
     if (relativeStart < 0) {
         k = length + relativeStart;
@@ -323,8 +323,9 @@ function fill(value /* [, start [, end]] */)
             k = length;
     }
     var relativeEnd = length;
             k = length;
     }
     var relativeEnd = length;
-    if (arguments.length > 2 && arguments[2] !== @undefined)
-        relativeEnd = arguments[2] | 0;
+    var end = @argument(2);
+    if (end !== @undefined)
+        relativeEnd = @toInteger(end);
     var final = 0;
     if (relativeEnd < 0) {
         final = length + relativeEnd;
     var final = 0;
     if (relativeEnd < 0) {
         final = length + relativeEnd;
@@ -353,7 +354,7 @@ function find(callback /*, thisArg */)
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.find callback must be a function");
     
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.find callback must be a function");
     
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
     for (var i = 0; i < length; i++) {
         var kValue = array[i];
         if (callback.@call(thisArg, kValue, i, array))
     for (var i = 0; i < length; i++) {
         var kValue = array[i];
         if (callback.@call(thisArg, kValue, i, array))
@@ -375,7 +376,7 @@ function findIndex(callback /*, thisArg */)
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.findIndex callback must be a function");
     
     if (typeof callback !== "function")
         @throwTypeError("Array.prototype.findIndex callback must be a function");
     
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
     for (var i = 0; i < length; i++) {
         if (callback.@call(thisArg, array[i], i, array))
             return i;
     for (var i = 0; i < length; i++) {
         if (callback.@call(thisArg, array[i], i, array))
             return i;
@@ -397,8 +398,9 @@ function includes(searchElement /*, fromIndex*/)
         return false;
 
     var fromIndex = 0;
         return false;
 
     var fromIndex = 0;
-    if (arguments.length > 1 && arguments[1] !== @undefined)
-        fromIndex = @toInteger(arguments[1]);
+    var from = @argument(1);
+    if (from !== @undefined)
+        fromIndex = @toInteger(from);
 
     var index;
     if (fromIndex >= 0)
 
     var index;
     if (fromIndex >= 0)
@@ -752,14 +754,11 @@ function copyWithin(target, start /*, end */)
     var from = (relativeStart < 0) ? maxWithPositives(length + relativeStart, 0) : minWithMaybeNegativeZeroAndPositive(relativeStart, length);
 
     var relativeEnd;
     var from = (relativeStart < 0) ? maxWithPositives(length + relativeStart, 0) : minWithMaybeNegativeZeroAndPositive(relativeStart, length);
 
     var relativeEnd;
-    if (arguments.length >= 3) {
-        var end = arguments[2];
-        if (end === @undefined)
-            relativeEnd = length;
-        else
-            relativeEnd = @toInteger(end);
-    } else
+    var end = @argument(2);
+    if (end === @undefined)
         relativeEnd = length;
         relativeEnd = length;
+    else
+        relativeEnd = @toInteger(end);
 
     var finalValue = (relativeEnd < 0) ? maxWithPositives(length + relativeEnd, 0) : minWithMaybeNegativeZeroAndPositive(relativeEnd, length);
 
 
     var finalValue = (relativeEnd < 0) ? maxWithPositives(length + relativeEnd, 0) : minWithMaybeNegativeZeroAndPositive(relativeEnd, length);
 
index 99afb26..234f185 100644 (file)
@@ -75,8 +75,8 @@ function toLocaleString(/* locales, options */)
     if (@isNaN(value))
         return "Invalid Date";
 
     if (@isNaN(value))
         return "Invalid Date";
 
-    var options = toDateTimeOptionsAnyAll(arguments[1]);
-    var locales = arguments[0];
+    var options = toDateTimeOptionsAnyAll(@argument(1));
+    var locales = @argument(0);
 
     var dateFormat = new @DateTimeFormat(locales, options);
     return dateFormat.format(value);
 
     var dateFormat = new @DateTimeFormat(locales, options);
     return dateFormat.format(value);
@@ -125,8 +125,8 @@ function toLocaleDateString(/* locales, options */)
     if (@isNaN(value))
         return "Invalid Date";
 
     if (@isNaN(value))
         return "Invalid Date";
 
-    var options = toDateTimeOptionsDateDate(arguments[1]);
-    var locales = arguments[0];
+    var options = toDateTimeOptionsDateDate(@argument(1));
+    var locales = @argument(0);
 
     var dateFormat = new @DateTimeFormat(locales, options);
     return dateFormat.format(value);
 
     var dateFormat = new @DateTimeFormat(locales, options);
     return dateFormat.format(value);
@@ -174,8 +174,8 @@ function toLocaleTimeString(/* locales, options */)
     if (@isNaN(value))
         return "Invalid Date";
 
     if (@isNaN(value))
         return "Invalid Date";
 
-    var options = toDateTimeOptionsTimeTime(arguments[1]);
-    var locales = arguments[0];
+    var options = toDateTimeOptionsTimeTime(@argument(1));
+    var locales = @argument(0);
 
     var dateFormat = new @DateTimeFormat(locales, options);
     return dateFormat.format(value);
 
     var dateFormat = new @DateTimeFormat(locales, options);
     return dateFormat.format(value);
index 79a6320..8302602 100644 (file)
@@ -33,7 +33,7 @@ function forEach(callback /*, thisArg */)
     if (typeof callback !== 'function')
         @throwTypeError("Map.prototype.forEach callback must be a function");
 
     if (typeof callback !== 'function')
         @throwTypeError("Map.prototype.forEach callback must be a function");
 
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
     var iterator = @MapIterator(this);
 
     // To avoid object allocations for iterator result objects, we pass the placeholder to the special "next" function in order to fill the results.
     var iterator = @MapIterator(this);
 
     // To avoid object allocations for iterator result objects, we pass the placeholder to the special "next" function in order to fill the results.
index 43790ad..435ea78 100644 (file)
@@ -38,7 +38,7 @@ function toLocaleString(/* locales, options */)
 
     // 3. Let numberFormat be Construct(%NumberFormat%, «locales, options»).
     // 4. ReturnIfAbrupt(numberFormat).
 
     // 3. Let numberFormat be Construct(%NumberFormat%, «locales, options»).
     // 4. ReturnIfAbrupt(numberFormat).
-    var numberFormat = new @NumberFormat(arguments[0], arguments[1]);
+    var numberFormat = new @NumberFormat(@argument(0), @argument(1));
 
     // 5. Return FormatNumber(numberFormat, x).
     return numberFormat.format(number);
 
     // 5. Return FormatNumber(numberFormat, x).
     return numberFormat.format(number);
index 8345f2e..e9b6626 100644 (file)
@@ -33,7 +33,7 @@ function forEach(callback /*, thisArg */)
     if (typeof callback !== 'function')
         @throwTypeError("Set.prototype.forEach callback must be a function");
 
     if (typeof callback !== 'function')
         @throwTypeError("Set.prototype.forEach callback must be a function");
 
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
     var iterator = @SetIterator(this);
 
     // To avoid object allocations for iterator result objects, we pass the placeholder to the special "next" function in order to fill the results.
     var iterator = @SetIterator(this);
 
     // To avoid object allocations for iterator result objects, we pass the placeholder to the special "next" function in order to fill the results.
index 1cef4f9..c9cdfcf 100644 (file)
@@ -131,7 +131,7 @@ function padStart(maxLength/*, fillString*/)
         return string;
 
     var filler;
         return string;
 
     var filler;
-    var fillString = arguments[1];
+    var fillString = @argument(1);
     if (fillString === @undefined)
         filler = " ";
     else {
     if (fillString === @undefined)
         filler = " ";
     else {
@@ -168,7 +168,7 @@ function padEnd(maxLength/*, fillString*/)
         return string;
 
     var filler;
         return string;
 
     var filler;
-    var fillString = arguments[1];
+    var fillString = @argument(1);
     if (fillString === @undefined)
         filler = " ";
     else {
     if (fillString === @undefined)
         filler = " ";
     else {
@@ -252,12 +252,14 @@ function localeCompare(that/*, locales, options */)
     var thatString = @toString(that);
 
     // Avoid creating a collator for defaults.
     var thatString = @toString(that);
 
     // Avoid creating a collator for defaults.
-    if (arguments[1] === @undefined && arguments[2] === @undefined)
+    var locales = @argument(1);
+    var options = @argument(2);
+    if (locales === @undefined && options === @undefined)
         return @Collator.prototype.compare(thisString, thatString);
 
     // 6. Let collator be Construct(%Collator%, «locales, options»).
     // 7. ReturnIfAbrupt(collator).
         return @Collator.prototype.compare(thisString, thatString);
 
     // 6. Let collator be Construct(%Collator%, «locales, options»).
     // 7. ReturnIfAbrupt(collator).
-    var collator = new @Collator(arguments[1], arguments[2]);
+    var collator = new @Collator(locales, options);
 
     // 8. Return CompareStrings(collator, S, That).
     return collator.compare(thisString, thatString);
 
     // 8. Return CompareStrings(collator, S, That).
     return collator.compare(thisString, thatString);
index 1763b20..54a957b 100644 (file)
@@ -48,7 +48,7 @@ function from(items /* [ , mapfn [ , thisArg ] ] */)
 {
     "use strict";
 
 {
     "use strict";
 
-    let mapFn = arguments[1];
+    let mapFn = @argument(1);
 
     let thisArg;
 
 
     let thisArg;
 
@@ -56,8 +56,7 @@ function from(items /* [ , mapfn [ , thisArg ] ] */)
         if (typeof mapFn !== "function")
             @throwTypeError("TypedArray.from requires that the second argument, when provided, be a function");
 
         if (typeof mapFn !== "function")
             @throwTypeError("TypedArray.from requires that the second argument, when provided, be a function");
 
-        if (arguments.length > 2)
-            thisArg = arguments[2];
+        thisArg = @argument(2);
     }
 
     if (items == null)
     }
 
     if (items == null)
index dbe06b9..53674bf 100644 (file)
@@ -93,7 +93,7 @@ function every(callback /*, thisArg */)
 {
     "use strict";
     var length = @typedArrayLength(this);
 {
     "use strict";
     var length = @typedArrayLength(this);
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.every callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.every callback must be a function");
@@ -111,15 +111,9 @@ function fill(value /* [, start [, end]] */)
     "use strict";
 
     let length = @typedArrayLength(this);
     "use strict";
 
     let length = @typedArrayLength(this);
-    let start;
-    let end;
 
 
-    if (arguments.length > 1) {
-        start = arguments[1];
-        if (arguments.length > 2) {
-            end = arguments[2];
-        }
-    }
+    let start = @argument(1);
+    let end = @argument(2);
 
     start = @typedArrayClampArgumentToStartOrEnd(start, length, 0);
     end = @typedArrayClampArgumentToStartOrEnd(end, length, length);
 
     start = @typedArrayClampArgumentToStartOrEnd(start, length, 0);
     end = @typedArrayClampArgumentToStartOrEnd(end, length, length);
@@ -133,7 +127,7 @@ function find(callback /* [, thisArg] */)
 {
     "use strict";
     var length = @typedArrayLength(this);
 {
     "use strict";
     var length = @typedArrayLength(this);
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.find callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.find callback must be a function");
@@ -150,7 +144,7 @@ function findIndex(callback /* [, thisArg] */)
 {
     "use strict";
     var length = @typedArrayLength(this);
 {
     "use strict";
     var length = @typedArrayLength(this);
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.findIndex callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.findIndex callback must be a function");
@@ -166,7 +160,7 @@ function forEach(callback /* [, thisArg] */)
 {
     "use strict";
     var length = @typedArrayLength(this);
 {
     "use strict";
     var length = @typedArrayLength(this);
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.forEach callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.forEach callback must be a function");
@@ -180,7 +174,7 @@ function some(callback /* [, thisArg] */)
     // 22.2.3.24
     "use strict";
     var length = @typedArrayLength(this);
     // 22.2.3.24
     "use strict";
     var length = @typedArrayLength(this);
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.some callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.some callback must be a function");
@@ -285,12 +279,13 @@ function reduce(callback /* [, initialValue] */)
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.reduce callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.reduce callback must be a function");
 
-    if (length === 0 && arguments.length < 2)
+    var argumentCount = @argumentCount();
+    if (length === 0 && argumentCount < 2)
         @throwTypeError("TypedArray.prototype.reduce of empty array with no initial value");
 
     var accumulator, k = 0;
         @throwTypeError("TypedArray.prototype.reduce of empty array with no initial value");
 
     var accumulator, k = 0;
-    if (arguments.length > 1)
-        accumulator = arguments[1];
+    if (argumentCount > 1)
+        accumulator = @argument(1);
     else
         accumulator = this[k++];
 
     else
         accumulator = this[k++];
 
@@ -310,12 +305,13 @@ function reduceRight(callback /* [, initialValue] */)
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.reduceRight callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.reduceRight callback must be a function");
 
-    if (length === 0 && arguments.length < 2)
+    var argumentCount = @argumentCount();
+    if (length === 0 && argumentCount < 2)
         @throwTypeError("TypedArray.prototype.reduceRight of empty array with no initial value");
 
     var accumulator, k = length - 1;
         @throwTypeError("TypedArray.prototype.reduceRight of empty array with no initial value");
 
     var accumulator, k = length - 1;
-    if (arguments.length > 1)
-        accumulator = arguments[1];
+    if (argumentCount > 1)
+        accumulator = @argument(1);
     else
         accumulator = this[k--];
 
     else
         accumulator = this[k--];
 
@@ -335,7 +331,7 @@ function map(callback /*, thisArg */)
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.map callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.map callback must be a function");
 
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
 
     // Do species construction
     var constructor = this.constructor;
 
     // Do species construction
     var constructor = this.constructor;
@@ -369,7 +365,7 @@ function filter(callback /*, thisArg */)
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.filter callback must be a function");
 
     if (typeof callback !== "function")
         @throwTypeError("TypedArray.prototype.filter callback must be a function");
 
-    var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
+    var thisArg = @argument(1);
     var kept = [];
 
     for (var i = 0; i < length; i++) {
     var kept = [];
 
     for (var i = 0; i < length; i++) {
index ff8ffa3..19e5d7c 100644 (file)
@@ -38,6 +38,7 @@ class RegisterID;
 class Identifier;
 
 #define JSC_COMMON_BYTECODE_INTRINSIC_FUNCTIONS_EACH_NAME(macro) \
 class Identifier;
 
 #define JSC_COMMON_BYTECODE_INTRINSIC_FUNCTIONS_EACH_NAME(macro) \
+    macro(argument) \
     macro(argumentCount) \
     macro(assert) \
     macro(isObject) \
     macro(argumentCount) \
     macro(assert) \
     macro(isObject) \
index 84ae3bd..28467ea 100644 (file)
@@ -9,6 +9,7 @@
             { "name" : "op_create_scoped_arguments", "length" : 3 },
             { "name" : "op_create_cloned_arguments", "length" : 2 },
             { "name" : "op_create_this", "length" : 5 },
             { "name" : "op_create_scoped_arguments", "length" : 3 },
             { "name" : "op_create_cloned_arguments", "length" : 2 },
             { "name" : "op_create_this", "length" : 5 },
+            { "name" : "op_get_argument", "length" : 4 },
             { "name" : "op_argument_count", "length" : 2 },
             { "name" : "op_to_this", "length" : 4 },
             { "name" : "op_check_tdz", "length" : 2 },
             { "name" : "op_argument_count", "length" : 2 },
             { "name" : "op_to_this", "length" : 4 },
             { "name" : "op_check_tdz", "length" : 2 },
index 162db28..9907148 100644 (file)
@@ -54,6 +54,7 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_create_cloned_arguments:
     case op_get_rest_length:
     case op_watchdog:
     case op_create_cloned_arguments:
     case op_get_rest_length:
     case op_watchdog:
+    case op_get_argument:
         return;
     case op_assert:
     case op_get_scope:
         return;
     case op_assert:
     case op_get_scope:
@@ -462,6 +463,7 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_del_by_val:
     case op_unsigned:
     case op_get_from_arguments: 
     case op_del_by_val:
     case op_unsigned:
     case op_get_from_arguments: 
+    case op_get_argument:
     case op_create_rest:
     case op_get_rest_length: {
         ASSERT(opcodeLengths[opcodeID] > 1);
     case op_create_rest:
     case op_get_rest_length: {
         ASSERT(opcodeLengths[opcodeID] > 1);
index 785d8e0..9e1d919 100644 (file)
@@ -773,6 +773,14 @@ void CodeBlock::dumpBytecode(
             printLocationOpAndRegisterOperand(out, exec, location, it, "argument_count", r0);
             break;
         }
             printLocationOpAndRegisterOperand(out, exec, location, it, "argument_count", r0);
             break;
         }
+        case op_get_argument: {
+            int r0 = (++it)->u.operand;
+            int index = (++it)->u.operand;
+            printLocationOpAndRegisterOperand(out, exec, location, it, "argument", r0);
+            out.printf(", %d", index);
+            dumpValueProfiling(out, it, hasPrintedProfiling);
+            break;
+        }
         case op_create_rest: {
             int r0 = (++it)->u.operand;
             int r1 = (++it)->u.operand;
         case op_create_rest: {
             int r0 = (++it)->u.operand;
             int r1 = (++it)->u.operand;
@@ -2071,7 +2079,8 @@ void CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
         case op_try_get_by_id:
         case op_get_by_val_with_this:
         case op_get_from_arguments:
         case op_try_get_by_id:
         case op_get_by_val_with_this:
         case op_get_from_arguments:
-        case op_to_number: {
+        case op_to_number:
+        case op_get_argument: {
             linkValueProfile(i, opLength);
             break;
         }
             linkValueProfile(i, opLength);
             break;
         }
index 2b3c087..04e4cab 100644 (file)
@@ -2839,6 +2839,15 @@ RegisterID* BytecodeGenerator::emitAssert(RegisterID* condition, int line)
     return condition;
 }
 
     return condition;
 }
 
+RegisterID* BytecodeGenerator::emitGetArgument(RegisterID* dst, int32_t index)
+{
+    UnlinkedValueProfile profile = emitProfiledOpcode(op_get_argument);
+    instructions().append(dst->index());
+    instructions().append(index + 1); // Including |this|.
+    instructions().append(profile);
+    return dst;
+}
+
 RegisterID* BytecodeGenerator::emitCreateThis(RegisterID* dst)
 {
     size_t begin = instructions().size();
 RegisterID* BytecodeGenerator::emitCreateThis(RegisterID* dst)
 {
     size_t begin = instructions().size();
index 7c01729..6346118 100644 (file)
@@ -577,6 +577,8 @@ namespace JSC {
         void emitPutGetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* getter);
         void emitPutSetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* setter);
 
         void emitPutGetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* getter);
         void emitPutSetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* setter);
 
+        RegisterID* emitGetArgument(RegisterID* dst, int32_t index);
+
         // Initialize object with generator fields (@generatorThis, @generatorNext, @generatorState, @generatorFrame)
         void emitPutGeneratorFields(RegisterID* nextFunction);
 
         // Initialize object with generator fields (@generatorThis, @generatorNext, @generatorState, @generatorFrame)
         void emitPutGeneratorFields(RegisterID* nextFunction);
 
index ef2c5e6..65c86e8 100644 (file)
@@ -860,6 +860,23 @@ RegisterID* BytecodeIntrinsicNode::emitBytecode(BytecodeGenerator& generator, Re
     return (this->*m_emitter)(generator, dst);
 }
 
     return (this->*m_emitter)(generator, dst);
 }
 
+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_argument(BytecodeGenerator& generator, RegisterID* dst)
+{
+    ArgumentListNode* node = m_args->m_listNode;
+    ASSERT(node->m_expr->isNumber());
+    double value = static_cast<NumberNode*>(node->m_expr)->value();
+    int32_t index = static_cast<int32_t>(value);
+    ASSERT(value == index);
+    ASSERT(index >= 0);
+    ASSERT(!node->m_next);
+
+    // The body functions of generator and async have different mechanism for arguments.
+    ASSERT(generator.parseMode() != SourceParseMode::GeneratorBodyMode);
+    ASSERT(!isAsyncFunctionBodyParseMode(generator.parseMode()));
+
+    return generator.emitGetArgument(generator.finalDestination(dst), index);
+}
+
 RegisterID* BytecodeIntrinsicNode::emit_intrinsic_argumentCount(BytecodeGenerator& generator, RegisterID* dst)
 {
     ASSERT(!m_args->m_listNode);
 RegisterID* BytecodeIntrinsicNode::emit_intrinsic_argumentCount(BytecodeGenerator& generator, RegisterID* dst)
 {
     ASSERT(!m_args->m_listNode);
index fc5bf97..62b1da7 100644 (file)
@@ -2120,6 +2120,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case PutToArguments:
         break;
 
     case PutToArguments:
         break;
 
+    case GetArgument:
+        forNode(node).makeHeapTop();
+        break;
+
     case TryGetById:
         // FIXME: This should constant fold at least as well as the normal GetById case.
         // https://bugs.webkit.org/show_bug.cgi?id=156422
     case TryGetById:
         // FIXME: This should constant fold at least as well as the normal GetById case.
         // https://bugs.webkit.org/show_bug.cgi?id=156422
index 1762995..943db8d 100644 (file)
@@ -5303,6 +5303,22 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 get(VirtualRegister(currentInstruction[3].u.operand)));
             NEXT_OPCODE(op_put_to_arguments);
         }
                 get(VirtualRegister(currentInstruction[3].u.operand)));
             NEXT_OPCODE(op_put_to_arguments);
         }
+
+        case op_get_argument: {
+            InlineCallFrame* inlineCallFrame = this->inlineCallFrame();
+            Node* argument;
+            int32_t argumentIndexIncludingThis = currentInstruction[2].u.operand;
+            if (inlineCallFrame && !inlineCallFrame->isVarargs()) {
+                int32_t argumentCountIncludingThis = inlineCallFrame->arguments.size();
+                if (argumentIndexIncludingThis < argumentCountIncludingThis)
+                    argument = get(virtualRegisterForArgument(argumentIndexIncludingThis));
+                else
+                    argument = addToGraph(JSConstant, OpInfo(m_constantUndefined));
+            } else
+                argument = addToGraph(GetArgument, OpInfo(argumentIndexIncludingThis), OpInfo(getPrediction()));
+            set(VirtualRegister(currentInstruction[1].u.operand), argument);
+            NEXT_OPCODE(op_get_argument);
+        }
             
         case op_new_func:
         case op_new_generator_func: {
             
         case op_new_func:
         case op_new_generator_func: {
index c323d46..6023782 100644 (file)
@@ -214,6 +214,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_create_cloned_arguments:
     case op_get_from_arguments:
     case op_put_to_arguments:
     case op_create_cloned_arguments:
     case op_get_from_arguments:
     case op_put_to_arguments:
+    case op_get_argument:
     case op_jneq_ptr:
     case op_typeof:
     case op_to_number:
     case op_jneq_ptr:
     case op_typeof:
     case op_to_number:
index e5d2172..16ffd51 100644 (file)
@@ -1130,6 +1130,13 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         def(HeapLocation(DirectArgumentsLoc, heap, node->child1()), LazyNode(node->child2().node()));
         return;
     }
         def(HeapLocation(DirectArgumentsLoc, heap, node->child1()), LazyNode(node->child2().node()));
         return;
     }
+
+    case GetArgument: {
+        read(Stack);
+        // FIXME: It would be trivial to have a def here.
+        // https://bugs.webkit.org/show_bug.cgi?id=143077
+        return;
+    }
         
     case GetGlobalVar:
     case GetGlobalLexicalVariable:
         
     case GetGlobalVar:
     case GetGlobalLexicalVariable:
index 9e6aa55..e5ad6d4 100644 (file)
@@ -258,6 +258,7 @@ bool doesGC(Graph& graph, Node* node)
     case GetStack:
     case GetFromArguments:
     case PutToArguments:
     case GetStack:
     case GetFromArguments:
     case PutToArguments:
+    case GetArgument:
     case LogShadowChickenPrologue:
     case LogShadowChickenTail:
     case GetDynamicVar:
     case LogShadowChickenPrologue:
     case LogShadowChickenTail:
     case GetDynamicVar:
index 848e1f4..d45be8a 100644 (file)
@@ -1746,6 +1746,7 @@ private:
         case GetCallee:
         case GetArgumentCountIncludingThis:
         case GetRestLength:
         case GetCallee:
         case GetArgumentCountIncludingThis:
         case GetRestLength:
+        case GetArgument:
         case Flush:
         case PhantomLocal:
         case GetLocalUnlinked:
         case Flush:
         case PhantomLocal:
         case GetLocalUnlinked:
index e6f5a59..3dd2358 100644 (file)
@@ -236,6 +236,8 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node, DumpContext*
         out.print(comma, node->scopeOffset());
     if (node->hasDirectArgumentsOffset())
         out.print(comma, node->capturedArgumentsOffset());
         out.print(comma, node->scopeOffset());
     if (node->hasDirectArgumentsOffset())
         out.print(comma, node->capturedArgumentsOffset());
+    if (node->hasArgumentIndex())
+        out.print(comma, node->argumentIndex());
     if (node->hasRegisterPointer())
         out.print(comma, "global", "(", RawPointer(node->variablePointer()), ")");
     if (node->hasIdentifier())
     if (node->hasRegisterPointer())
         out.print(comma, "global", "(", RawPointer(node->variablePointer()), ")");
     if (node->hasIdentifier())
index 54c453f..5715b9e 100644 (file)
@@ -1456,6 +1456,7 @@ public:
         case MultiGetByOffset:
         case GetClosureVar:
         case GetFromArguments:
         case MultiGetByOffset:
         case GetClosureVar:
         case GetFromArguments:
+        case GetArgument:
         case ArrayPop:
         case ArrayPush:
         case RegExpExec:
         case ArrayPop:
         case ArrayPush:
         case RegExpExec:
@@ -2425,6 +2426,17 @@ public:
         return m_opInfo.as<unsigned>();
     }
 
         return m_opInfo.as<unsigned>();
     }
 
+    bool hasArgumentIndex()
+    {
+        return op() == GetArgument;
+    }
+
+    unsigned argumentIndex()
+    {
+        ASSERT(hasArgumentIndex());
+        return m_opInfo.as<unsigned>();
+    }
+
     void dumpChildren(PrintStream& out)
     {
         if (!child1())
     void dumpChildren(PrintStream& out)
     {
         if (!child1())
index f37423c..9666d59 100644 (file)
@@ -348,6 +348,7 @@ namespace JSC { namespace DFG {
     macro(PhantomClonedArguments, NodeResultJS | NodeMustGenerate) \
     macro(GetFromArguments, NodeResultJS) \
     macro(PutToArguments, NodeMustGenerate) \
     macro(PhantomClonedArguments, NodeResultJS | NodeMustGenerate) \
     macro(GetFromArguments, NodeResultJS) \
     macro(PutToArguments, NodeMustGenerate) \
+    macro(GetArgument, NodeResultJS) \
     \
     macro(NewFunction, NodeResultJS) \
     \
     \
     macro(NewFunction, NodeResultJS) \
     \
index 6fbd7bd..56bc6d4 100644 (file)
@@ -116,7 +116,7 @@ private:
         case TailCallForwardVarargsInlinedCaller: {
 
             InlineCallFrame* inlineCallFrame;
         case TailCallForwardVarargsInlinedCaller: {
 
             InlineCallFrame* inlineCallFrame;
-            if (m_node->argumentsChild())
+            if (m_node->hasArgumentsChild() && m_node->argumentsChild())
                 inlineCallFrame = m_node->argumentsChild()->origin.semantic.inlineCallFrame;
             else
                 inlineCallFrame = m_node->origin.semantic.inlineCallFrame;
                 inlineCallFrame = m_node->argumentsChild()->origin.semantic.inlineCallFrame;
             else
                 inlineCallFrame = m_node->origin.semantic.inlineCallFrame;
@@ -143,6 +143,24 @@ private:
                 m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount));
             break;
         }
                 m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount));
             break;
         }
+
+        case GetArgument: {
+            InlineCallFrame* inlineCallFrame = m_node->origin.semantic.inlineCallFrame;
+            unsigned indexIncludingThis = m_node->argumentIndex();
+            if (!inlineCallFrame) {
+                if (indexIncludingThis < static_cast<unsigned>(m_graph.m_codeBlock->numParameters()))
+                    m_read(virtualRegisterForArgument(indexIncludingThis));
+                m_read(VirtualRegister(CallFrameSlot::argumentCount));
+                break;
+            }
+
+            ASSERT_WITH_MESSAGE(inlineCallFrame->isVarargs(), "GetArgument is only used for InlineCallFrame if the call frame is varargs.");
+            if (indexIncludingThis < inlineCallFrame->arguments.size())
+                m_read(VirtualRegister(inlineCallFrame->stackOffset + virtualRegisterForArgument(indexIncludingThis).offset()));
+            m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount));
+            break;
+        }
+
             
         default: {
             // All of the outermost arguments, except this, are definitely read.
             
         default: {
             // All of the outermost arguments, except this, are definitely read.
index 20531a0..f05077a 100644 (file)
@@ -710,6 +710,7 @@ private:
         case GetFromArguments:
         case LoadFromJSMapBucket:
         case ToNumber:
         case GetFromArguments:
         case LoadFromJSMapBucket:
         case ToNumber:
+        case GetArgument:
         case CallDOMGetter: {
             setPrediction(m_currentNode->getHeapPrediction());
             break;
         case CallDOMGetter: {
             setPrediction(m_currentNode->getHeapPrediction());
             break;
index 3fe9dc1..a8545cd 100644 (file)
@@ -298,6 +298,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case CreateScopedArguments:
     case CreateClonedArguments:
     case GetFromArguments:
     case CreateScopedArguments:
     case CreateClonedArguments:
     case GetFromArguments:
+    case GetArgument:
     case PutToArguments:
     case NewFunction:
     case NewGeneratorFunction:
     case PutToArguments:
     case NewFunction:
     case NewGeneratorFunction:
index 3419b70..3eb730f 100644 (file)
@@ -167,11 +167,7 @@ void SpeculativeJIT::emitGetLength(InlineCallFrame* inlineCallFrame, GPRReg leng
     if (inlineCallFrame && !inlineCallFrame->isVarargs())
         m_jit.move(TrustedImm32(inlineCallFrame->arguments.size() - !includeThis), lengthGPR);
     else {
     if (inlineCallFrame && !inlineCallFrame->isVarargs())
         m_jit.move(TrustedImm32(inlineCallFrame->arguments.size() - !includeThis), lengthGPR);
     else {
-        VirtualRegister argumentCountRegister;
-        if (!inlineCallFrame)
-            argumentCountRegister = VirtualRegister(CallFrameSlot::argumentCount);
-        else
-            argumentCountRegister = inlineCallFrame->argumentCountRegister;
+        VirtualRegister argumentCountRegister = m_jit.argumentCount(inlineCallFrame);
         m_jit.load32(JITCompiler::payloadFor(argumentCountRegister), lengthGPR);
         if (!includeThis)
             m_jit.sub32(TrustedImm32(1), lengthGPR);
         m_jit.load32(JITCompiler::payloadFor(argumentCountRegister), lengthGPR);
         if (!includeThis)
             m_jit.sub32(TrustedImm32(1), lengthGPR);
@@ -6631,11 +6627,7 @@ void SpeculativeJIT::compileCreateDirectArguments(Node* node)
         length.adopt(realLength);
         lengthGPR = length.gpr();
 
         length.adopt(realLength);
         lengthGPR = length.gpr();
 
-        VirtualRegister argumentCountRegister;
-        if (!node->origin.semantic.inlineCallFrame)
-            argumentCountRegister = VirtualRegister(CallFrameSlot::argumentCount);
-        else
-            argumentCountRegister = node->origin.semantic.inlineCallFrame->argumentCountRegister;
+        VirtualRegister argumentCountRegister = m_jit.argumentCount(node->origin.semantic);
         m_jit.load32(JITCompiler::payloadFor(argumentCountRegister), lengthGPR);
         m_jit.sub32(TrustedImm32(1), lengthGPR);
     }
         m_jit.load32(JITCompiler::payloadFor(argumentCountRegister), lengthGPR);
         m_jit.sub32(TrustedImm32(1), lengthGPR);
     }
@@ -6775,6 +6767,24 @@ void SpeculativeJIT::compilePutToArguments(Node* node)
     noResult(node);
 }
 
     noResult(node);
 }
 
+void SpeculativeJIT::compileGetArgument(Node* node)
+{
+    GPRTemporary argumentCount(this);
+    JSValueRegsTemporary result(this);
+    GPRReg argumentCountGPR = argumentCount.gpr();
+    JSValueRegs resultRegs = result.regs();
+    m_jit.load32(CCallHelpers::payloadFor(m_jit.argumentCount(node->origin.semantic)), argumentCountGPR);
+    auto argumentOutOfBounds = m_jit.branch32(CCallHelpers::LessThanOrEqual, argumentCountGPR, CCallHelpers::TrustedImm32(node->argumentIndex()));
+    m_jit.loadValue(CCallHelpers::addressFor(CCallHelpers::argumentsStart(node->origin.semantic) + node->argumentIndex() - 1), resultRegs);
+    auto done = m_jit.jump();
+
+    argumentOutOfBounds.link(&m_jit);
+    m_jit.moveValue(jsUndefined(), resultRegs);
+
+    done.link(&m_jit);
+    jsValueResult(resultRegs, node);
+}
+
 void SpeculativeJIT::compileCreateScopedArguments(Node* node)
 {
     SpeculateCellOperand scope(this, node->child1());
 void SpeculativeJIT::compileCreateScopedArguments(Node* node)
 {
     SpeculateCellOperand scope(this, node->child1());
index 796a8a3..50d9dbc 100644 (file)
@@ -2668,6 +2668,7 @@ public:
     void compileCreateDirectArguments(Node*);
     void compileGetFromArguments(Node*);
     void compilePutToArguments(Node*);
     void compileCreateDirectArguments(Node*);
     void compileGetFromArguments(Node*);
     void compilePutToArguments(Node*);
+    void compileGetArgument(Node*);
     void compileCreateScopedArguments(Node*);
     void compileCreateClonedArguments(Node*);
     void compileCreateRest(Node*);
     void compileCreateScopedArguments(Node*);
     void compileCreateClonedArguments(Node*);
     void compileCreateRest(Node*);
index 102429d..8c3ad4e 100644 (file)
@@ -4983,6 +4983,11 @@ void SpeculativeJIT::compile(Node* node)
         compilePutToArguments(node);
         break;
     }
         compilePutToArguments(node);
         break;
     }
+
+    case GetArgument: {
+        compileGetArgument(node);
+        break;
+    }
         
     case CreateScopedArguments: {
         compileCreateScopedArguments(node);
         
     case CreateScopedArguments: {
         compileCreateScopedArguments(node);
index 830bfad..40b1d17 100644 (file)
@@ -5155,6 +5155,11 @@ void SpeculativeJIT::compile(Node* node)
         compilePutToArguments(node);
         break;
     }
         compilePutToArguments(node);
         break;
     }
+
+    case GetArgument: {
+        compileGetArgument(node);
+        break;
+    }
         
     case CreateScopedArguments: {
         compileCreateScopedArguments(node);
         
     case CreateScopedArguments: {
         compileCreateScopedArguments(node);
index 3ba15e6..49965dd 100644 (file)
@@ -124,6 +124,7 @@ inline CapabilityLevel canCompile(Node* node)
     case CreateClonedArguments:
     case GetFromArguments:
     case PutToArguments:
     case CreateClonedArguments:
     case GetFromArguments:
     case PutToArguments:
+    case GetArgument:
     case InvalidationPoint:
     case StringCharAt:
     case CheckCell:
     case InvalidationPoint:
     case StringCharAt:
     case CheckCell:
index 79d97bb..e0197a1 100644 (file)
@@ -826,6 +826,9 @@ private:
         case PutToArguments:
             compilePutToArguments();
             break;
         case PutToArguments:
             compilePutToArguments();
             break;
+        case GetArgument:
+            compileGetArgument();
+            break;
         case CompareEq:
             compileCompareEq();
             break;
         case CompareEq:
             compileCompareEq();
             break;
@@ -3445,11 +3448,7 @@ private:
         if (inlineCallFrame && !inlineCallFrame->isVarargs())
             limit = m_out.constInt32(inlineCallFrame->arguments.size() - 1);
         else {
         if (inlineCallFrame && !inlineCallFrame->isVarargs())
             limit = m_out.constInt32(inlineCallFrame->arguments.size() - 1);
         else {
-            VirtualRegister argumentCountRegister;
-            if (!inlineCallFrame)
-                argumentCountRegister = VirtualRegister(CallFrameSlot::argumentCount);
-            else
-                argumentCountRegister = inlineCallFrame->argumentCountRegister;
+            VirtualRegister argumentCountRegister = AssemblyHelpers::argumentCount(inlineCallFrame);
             limit = m_out.sub(m_out.load32(payloadFor(argumentCountRegister)), m_out.int32One);
         }
         
             limit = m_out.sub(m_out.load32(payloadFor(argumentCountRegister)), m_out.int32One);
         }
         
@@ -5159,6 +5158,29 @@ private:
             lowCell(m_node->child1()),
             m_heaps.DirectArguments_storage[m_node->capturedArgumentsOffset().offset()]);
     }
             lowCell(m_node->child1()),
             m_heaps.DirectArguments_storage[m_node->capturedArgumentsOffset().offset()]);
     }
+
+    void compileGetArgument()
+    {
+        LValue argumentCount = m_out.load32(payloadFor(AssemblyHelpers::argumentCount(m_node->origin.semantic)));
+
+        LBasicBlock inBounds = m_out.newBlock();
+        LBasicBlock outOfBounds = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        m_out.branch(m_out.lessThanOrEqual(argumentCount, m_out.constInt32(m_node->argumentIndex())), unsure(outOfBounds), unsure(inBounds));
+
+        LBasicBlock lastNext = m_out.appendTo(inBounds, outOfBounds);
+        VirtualRegister arg = AssemblyHelpers::argumentsStart(m_node->origin.semantic) + m_node->argumentIndex() - 1;
+        ValueFromBlock inBoundsResult = m_out.anchor(m_out.load64(addressFor(arg)));
+        m_out.jump(continuation);
+
+        m_out.appendTo(outOfBounds, continuation);
+        ValueFromBlock outOfBoundsResult = m_out.anchor(m_out.constInt64(ValueUndefined));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setJSValue(m_out.phi(Int64, inBoundsResult, outOfBoundsResult));
+    }
     
     void compileCompareEq()
     {
     
     void compileCompareEq()
     {
index a99f98f..94d8d3f 100644 (file)
@@ -1290,6 +1290,19 @@ public:
     {
         return argumentsStart(codeOrigin.inlineCallFrame);
     }
     {
         return argumentsStart(codeOrigin.inlineCallFrame);
     }
+
+    static VirtualRegister argumentCount(InlineCallFrame* inlineCallFrame)
+    {
+        ASSERT(!inlineCallFrame || inlineCallFrame->isVarargs());
+        if (!inlineCallFrame)
+            return VirtualRegister(CallFrameSlot::argumentCount);
+        return inlineCallFrame->argumentCountRegister;
+    }
+
+    static VirtualRegister argumentCount(const CodeOrigin& codeOrigin)
+    {
+        return argumentCount(codeOrigin.inlineCallFrame);
+    }
     
     void emitLoadStructure(RegisterID source, RegisterID dest, RegisterID scratch);
 
     
     void emitLoadStructure(RegisterID source, RegisterID dest, RegisterID scratch);
 
index 49b1a86..143927a 100644 (file)
@@ -238,6 +238,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_create_direct_arguments)
         DEFINE_OP(op_create_scoped_arguments)
         DEFINE_OP(op_create_cloned_arguments)
         DEFINE_OP(op_create_direct_arguments)
         DEFINE_OP(op_create_scoped_arguments)
         DEFINE_OP(op_create_cloned_arguments)
+        DEFINE_OP(op_get_argument)
         DEFINE_OP(op_argument_count)
         DEFINE_OP(op_create_rest)
         DEFINE_OP(op_get_rest_length)
         DEFINE_OP(op_argument_count)
         DEFINE_OP(op_create_rest)
         DEFINE_OP(op_get_rest_length)
index 6d20249..6b656bc 100644 (file)
@@ -483,6 +483,7 @@ namespace JSC {
         void emit_op_create_direct_arguments(Instruction*);
         void emit_op_create_scoped_arguments(Instruction*);
         void emit_op_create_cloned_arguments(Instruction*);
         void emit_op_create_direct_arguments(Instruction*);
         void emit_op_create_scoped_arguments(Instruction*);
         void emit_op_create_cloned_arguments(Instruction*);
+        void emit_op_get_argument(Instruction*);
         void emit_op_argument_count(Instruction*);
         void emit_op_create_rest(Instruction*);
         void emit_op_get_rest_length(Instruction*);
         void emit_op_argument_count(Instruction*);
         void emit_op_create_rest(Instruction*);
         void emit_op_get_rest_length(Instruction*);
index 9f99aba..a3bd888 100644 (file)
@@ -1478,6 +1478,29 @@ void JIT::emit_op_get_rest_length(Instruction* currentInstruction)
 #endif
 }
 
 #endif
 }
 
+void JIT::emit_op_get_argument(Instruction* currentInstruction)
+{
+    int dst = currentInstruction[1].u.operand;
+    int index = currentInstruction[2].u.operand;
+#if USE(JSVALUE64)
+    JSValueRegs resultRegs(regT0);
+#else
+    JSValueRegs resultRegs(regT1, regT0);
+#endif
+
+    load32(payloadFor(CallFrameSlot::argumentCount), regT2);
+    Jump argumentOutOfBounds = branch32(LessThanOrEqual, regT2, TrustedImm32(index));
+    loadValue(addressFor(CallFrameSlot::thisArgument + index), resultRegs);
+    Jump done = jump();
+
+    argumentOutOfBounds.link(this);
+    moveValue(jsUndefined(), resultRegs);
+
+    done.link(this);
+    emitValueProfilingSite();
+    emitPutVirtualRegister(dst, resultRegs);
+}
+
 } // namespace JSC
 
 #endif // ENABLE(JIT)
 } // namespace JSC
 
 #endif // ENABLE(JIT)
index 784b3f0..17c3bcd 100644 (file)
@@ -672,6 +672,26 @@ _llint_op_enter:
     dispatch(1)
 
 
     dispatch(1)
 
 
+_llint_op_get_argument:
+    traceExecution()
+    loadisFromInstruction(1, t1)
+    loadisFromInstruction(2, t2)
+    loadi PayloadOffset + ArgumentCount[cfr], t0
+    bilteq t0, t2, .opGetArgumentOutOfBounds
+    loadi ThisArgumentOffset + TagOffset[cfr, t2, 8], t0
+    loadi ThisArgumentOffset + PayloadOffset[cfr, t2, 8], t3
+    storei t0, TagOffset[cfr, t1, 8]
+    storei t3, PayloadOffset[cfr, t1, 8]
+    valueProfile(t0, t3, 12, t1)
+    dispatch(4)
+
+.opGetArgumentOutOfBounds:
+    storei UndefinedTag, TagOffset[cfr, t1, 8]
+    storei 0, PayloadOffset[cfr, t1, 8]
+    valueProfile(UndefinedTag, 0, 12, t1)
+    dispatch(4)
+
+
 _llint_op_argument_count:
     traceExecution()
     loadisFromInstruction(1, t2)
 _llint_op_argument_count:
     traceExecution()
     loadisFromInstruction(1, t2)
index d804d90..fd0d376 100644 (file)
@@ -582,6 +582,23 @@ _llint_op_enter:
     dispatch(1)
 
 
     dispatch(1)
 
 
+_llint_op_get_argument:
+    traceExecution()
+    loadisFromInstruction(1, t1)
+    loadisFromInstruction(2, t2)
+    loadi PayloadOffset + ArgumentCount[cfr], t0
+    bilteq t0, t2, .opGetArgumentOutOfBounds
+    loadq ThisArgumentOffset[cfr, t2, 8], t0
+    storeq t0, [cfr, t1, 8]
+    valueProfile(t0, 3, t2)
+    dispatch(4)
+
+.opGetArgumentOutOfBounds:
+    storeq ValueUndefined, [cfr, t1, 8]
+    valueProfile(ValueUndefined, 3, t2)
+    dispatch(4)
+
+
 _llint_op_argument_count:
     traceExecution()
     loadisFromInstruction(1, t1)
 _llint_op_argument_count:
     traceExecution()
     loadisFromInstruction(1, t1)