[JSC] Improve our bound function implementation
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 22 Dec 2019 03:12:00 +0000 (03:12 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 22 Dec 2019 03:12:00 +0000 (03:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=205327

Reviewed by Keith Miller.

JSTests:

* microbenchmarks/function-bind-no-inlining-repeat-call.js: Added.
(assert):
(test):
(test2):
(foo):
(let.start.Date.now):
* stress/bind-args.js: Added.
(shouldBe):
(test):
(test2):

Source/JavaScriptCore:

This patch improves Function#bind, and calling bound function with bound arguments.

1. Rename CallFrameSlot::argumentCount to CallFrameSlot::argumentCountIncludingThis.
2. Do not include name in NativeExecutable for JSBoundFunction. Putting name in NativeExecutable is assuming that function + name pair is almost identical.
   This is true in host functions except for JSBoundFunction. JSBoundFunction should hold its name in JSBoundFunction.
3. Cache NativeExecutable for JSBoundFunction in the VM. We use a hash-map in JITThunk for NativeExecutables because we assume that host-function creation cannot be
   done by the user program: each executable is pre-defined to exactly one object by the environment, and there is no way to create host-functions repeatedly from
   the user-program. The only exception to this is JSBoundFunction so caching it on the VM avoids the hash-map lookup. This is not true for JSBoundFunction.
4. ThunkGenerator should support JSBoundFunction call with bound arguments. It turns out that Speedometer2/React-Redux-TodoMVC is using bound function with
   bound arguments. Additionally, it is used. This is really bad: when dispatching an event, we first call this function from C++, entering JS world,
   going back to C++ world again, and entering JS world to call bound function again. By using ThunkGenerator, we can eliminate this back and forth by directly
   calling the bound JS Executable from the thunk. Previously, bound arguments are stored in JSArray. But it is difficult to access them from thunk since we need to consider
   have-a-bad-time case. Instead, we use JSImmutableButterfly to save bound arguments so that JIT thunk can quickly access arguments. To capture arguments as
   JSImmutableButterfly in JS world, we introduce op_create_arguments_butterfly, and handle it in all tiers.
5. It turns out that eager materialization of "length" in JSBoundFunction takes long time while it is rarely used. This patch makes length lazily reified for JSBoundFunction.
6. To make Function.prototype.bind faster, we track whether "name" and "length" properties of JSFunction is modified or not. This skips has-own-length-property check, which
   makes Function.prototype.bind 11~% faster.

Combining things above, creation of JSBoundFunction is 80~% faster. And calling bound function with bound arguments is 3~x faster.
This improves Speedometer2/React-TodoMVC by ~3%.

* builtins/FunctionPrototype.js:
(bind):
* bytecode/AccessCase.cpp:
(JSC::AccessCase::generateImpl):
* bytecode/AccessCaseSnippetParams.cpp:
(JSC::SlowPathCallGeneratorWithArguments::generateImpl):
* bytecode/BytecodeIntrinsicRegistry.h:
* bytecode/BytecodeList.rb:
* bytecode/BytecodeUseDef.cpp:
(JSC::computeUsesForBytecodeIndexImpl):
(JSC::computeDefsForBytecodeIndexImpl):
* bytecode/VirtualRegister.cpp:
(JSC::VirtualRegister::dump const):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitCreateArgumentsButterfly):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::BytecodeIntrinsicNode::emit_intrinsic_createArgumentsButterfly):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArgumentsEliminationPhase.cpp:
* dfg/DFGArgumentsUtilities.cpp:
(JSC::DFG::argumentsInvolveStackSlot):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::flushImpl):
(JSC::DFG::ByteCodeParser::handleVarargsInlining):
(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/DFGGraph.cpp:
(JSC::DFG::Graph::isLiveInBytecode):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::forAllLocalsLiveInBytecode):
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::compileFunction):
* dfg/DFGJITCompiler.h:
(JSC::DFG::JITCompiler::emitStoreCallSiteIndex):
* dfg/DFGNodeType.h:
* dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
(JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):
* dfg/DFGOSRExit.cpp:
(JSC::DFG::emitRestoreArguments):
(JSC::DFG::reifyInlinedCallFrames):
(JSC::DFG::OSRExit::emitRestoreArguments):
* dfg/DFGOSRExitCompilerCommon.cpp:
(JSC::DFG::reifyInlinedCallFrames):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPreciseLocalClobberize.h:
(JSC::DFG::PreciseLocalClobberizeAdaptor::readTop):
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileCreateArgumentsButterfly):
(JSC::DFG::SpeculativeJIT::compileGetArgumentCountIncludingThis):
(JSC::DFG::SpeculativeJIT::compileSetArgumentCountIncludingThis):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::emitCall):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::emitCall):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStackLayoutPhase.cpp:
(JSC::DFG::StackLayoutPhase::run):
* dfg/DFGStoreBarrierInsertionPhase.cpp:
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLink.cpp:
(JSC::FTL::link):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::lower):
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCreateArgumentsButterfly):
(JSC::FTL::DFG::LowerDFGToB3::compileGetArgumentCountIncludingThis):
(JSC::FTL::DFG::LowerDFGToB3::compileSetArgumentCountIncludingThis):
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstruct):
(JSC::FTL::DFG::LowerDFGToB3::compileDirectCallOrConstruct):
(JSC::FTL::DFG::LowerDFGToB3::compileTailCall):
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargsSpread):
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargs):
(JSC::FTL::DFG::LowerDFGToB3::compileCallEval):
(JSC::FTL::DFG::LowerDFGToB3::getArgumentsLength):
(JSC::FTL::DFG::LowerDFGToB3::callPreflight):
* ftl/FTLSlowPathCall.h:
(JSC::FTL::callOperation):
* interpreter/CallFrame.cpp:
(JSC::CallFrame::callSiteAsRawBits const):
(JSC::CallFrame::unsafeCallSiteAsRawBits const):
(JSC::CallFrame::setCurrentVPC):
* interpreter/CallFrame.h:
(JSC::CallFrame::argumentCountIncludingThis const):
(JSC::CallFrame::setArgumentCountIncludingThis):
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::jitAssertArgumentCountSane):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::argumentCount):
* jit/CCallHelpers.h:
(JSC::CCallHelpers::prepareForTailCallSlow):
* jit/CallFrameShuffler.cpp:
(JSC::CallFrameShuffler::dump const):
(JSC::CallFrameShuffler::prepareForTailCall):
(JSC::CallFrameShuffler::prepareAny):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::compileWithoutLinking):
* jit/JITCall.cpp:
(JSC::JIT::compileSetupFrame):
(JSC::JIT::compileOpCall):
* jit/JITCall32_64.cpp:
(JSC::JIT::compileSetupFrame):
(JSC::JIT::compileOpCall):
* jit/JITInlines.h:
(JSC::JIT::updateTopCallFrame):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_argument_count):
(JSC::JIT::emit_op_get_rest_length):
(JSC::JIT::emit_op_get_argument):
* jit/SetupVarargsFrame.cpp:
(JSC::emitSetupVarargsFrameFastCase):
* jit/SpecializedThunkJIT.h:
(JSC::SpecializedThunkJIT::SpecializedThunkJIT):
* jit/ThunkGenerators.cpp:
(JSC::arityFixupGenerator):
(JSC::boundFunctionCallGenerator):
(JSC::boundThisNoArgsFunctionCallGenerator): Deleted.
* jit/ThunkGenerators.h:
* jsc.cpp:
* llint/LLIntData.cpp:
(JSC::LLInt::Data::performAssertions):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* llint/WebAssembly.asm:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
* runtime/ExecutableBase.h:
* runtime/FunctionRareData.cpp:
(JSC::FunctionRareData::FunctionRareData):
* runtime/FunctionRareData.h:
* runtime/IntlCollatorPrototype.cpp:
(JSC::IntlCollatorPrototypeGetterCompare):
* runtime/IntlDateTimeFormatPrototype.cpp:
(JSC::IntlDateTimeFormatPrototypeGetterFormat):
* runtime/IntlNumberFormatPrototype.cpp:
(JSC::IntlNumberFormatPrototypeGetterFormat):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSBoundFunction.cpp:
(JSC::boundThisNoArgsFunctionCall):
(JSC::boundFunctionCall):
(JSC::boundThisNoArgsFunctionConstruct):
(JSC::boundFunctionConstruct):
(JSC::JSBoundFunction::create):
(JSC::JSBoundFunction::JSBoundFunction):
(JSC::JSBoundFunction::boundArgsCopy):
(JSC::JSBoundFunction::visitChildren):
* runtime/JSBoundFunction.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::finishCreation):
(JSC::JSFunction::name):
(JSC::JSFunction::getOwnPropertySlot):
(JSC::JSFunction::getOwnNonIndexPropertyNames):
(JSC::JSFunction::put):
(JSC::JSFunction::deleteProperty):
(JSC::JSFunction::defineOwnProperty):
(JSC::JSFunction::reifyLength):
(JSC::JSFunction::reifyLazyPropertyIfNeeded):
(JSC::JSFunction::reifyLazyPropertyForHostOrBuiltinIfNeeded):
(JSC::JSFunction::reifyLazyBoundNameIfNeeded):
* runtime/JSFunction.h:
* runtime/JSFunctionInlines.h:
(JSC::JSFunction::areNameAndLengthOriginal):
* runtime/JSGlobalObject.cpp:
(JSC::makeBoundFunction):
(JSC::hasOwnLengthProperty):
* runtime/JSObject.h:
(JSC::getJSFunction):
(JSC::getCallData): Deleted.
(JSC::getConstructData): Deleted.
* runtime/JSObjectInlines.h:
(JSC::getCallData):
(JSC::getConstructData):
* runtime/VM.cpp:
(JSC::thunkGeneratorForIntrinsic):
(JSC::VM::getBoundFunction):
* runtime/VM.h:
* wasm/js/WasmToJS.cpp:
(JSC::Wasm::wasmToJS):
* wasm/js/WebAssemblyFunction.cpp:
(JSC::WebAssemblyFunction::jsCallEntrypointSlow):

Tools:

Support running slow-microbenchmarks.

* Scripts/run-jsc-benchmarks:

LayoutTests:

* inspector/model/remote-object-get-properties-expected.txt:
* inspector/runtime/getDisplayableProperties-expected.txt:
* inspector/runtime/getProperties-expected.txt:

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

95 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/function-bind-no-inlining-repeat-call.js [new file with mode: 0644]
JSTests/stress/bind-args.js [new file with mode: 0644]
LayoutTests/ChangeLog
LayoutTests/inspector/model/remote-object-get-properties-expected.txt
LayoutTests/inspector/runtime/getDisplayableProperties-expected.txt
LayoutTests/inspector/runtime/getProperties-expected.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/FunctionPrototype.js
Source/JavaScriptCore/bytecode/AccessCase.cpp
Source/JavaScriptCore/bytecode/AccessCaseSnippetParams.cpp
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
Source/JavaScriptCore/bytecode/BytecodeList.rb
Source/JavaScriptCore/bytecode/BytecodeUseDef.cpp
Source/JavaScriptCore/bytecode/VirtualRegister.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp
Source/JavaScriptCore/dfg/DFGArgumentsUtilities.cpp
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/DFGGraph.h
Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
Source/JavaScriptCore/dfg/DFGJITCompiler.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp
Source/JavaScriptCore/dfg/DFGOSRExit.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.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/dfg/DFGStackLayoutPhase.cpp
Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLink.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/ftl/FTLSlowPathCall.h
Source/JavaScriptCore/interpreter/CallFrame.cpp
Source/JavaScriptCore/interpreter/CallFrame.h
Source/JavaScriptCore/jit/AssemblyHelpers.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/CCallHelpers.h
Source/JavaScriptCore/jit/CallFrameShuffler.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JITCall.cpp
Source/JavaScriptCore/jit/JITCall32_64.cpp
Source/JavaScriptCore/jit/JITInlines.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/SetupVarargsFrame.cpp
Source/JavaScriptCore/jit/SpecializedThunkJIT.h
Source/JavaScriptCore/jit/ThunkGenerators.cpp
Source/JavaScriptCore/jit/ThunkGenerators.h
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/llint/LLIntData.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/llint/WebAssembly.asm
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.h
Source/JavaScriptCore/runtime/ExecutableBase.h
Source/JavaScriptCore/runtime/FunctionRareData.cpp
Source/JavaScriptCore/runtime/FunctionRareData.h
Source/JavaScriptCore/runtime/IntlCollatorPrototype.cpp
Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.cpp
Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp
Source/JavaScriptCore/runtime/Intrinsic.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSBoundFunction.cpp
Source/JavaScriptCore/runtime/JSBoundFunction.h
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/runtime/JSFunction.h
Source/JavaScriptCore/runtime/JSFunctionInlines.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/JSObjectInlines.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/wasm/js/WasmToJS.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
Tools/ChangeLog
Tools/Scripts/run-jsc-benchmarks

index ae7f16b..00c95b8 100644 (file)
@@ -1,3 +1,21 @@
+2019-12-21  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Improve our bound function implementation
+        https://bugs.webkit.org/show_bug.cgi?id=205327
+
+        Reviewed by Keith Miller.
+
+        * microbenchmarks/function-bind-no-inlining-repeat-call.js: Added.
+        (assert):
+        (test):
+        (test2):
+        (foo):
+        (let.start.Date.now):
+        * stress/bind-args.js: Added.
+        (shouldBe):
+        (test):
+        (test2):
+
 2019-12-17  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] 8Bit JSRopeString can contain 16Bit string in its rope
diff --git a/JSTests/microbenchmarks/function-bind-no-inlining-repeat-call.js b/JSTests/microbenchmarks/function-bind-no-inlining-repeat-call.js
new file mode 100644 (file)
index 0000000..c828f74
--- /dev/null
@@ -0,0 +1,36 @@
+
+function assert(b) {
+    if (!b)
+        throw new Error("Bad")
+}
+noInline(assert);
+
+function test(f, v, c, d) {
+    return f.bind(v, c, d);
+}
+noInline(test);
+
+function test2(f, v) {
+    return f.bind(v);
+}
+noInline(test);
+
+function foo(a,b,c,d,e,f) { return this; }
+let thisValue = {};
+let start = Date.now();
+
+{
+    let f = test(foo, thisValue, 20, 30);
+    for (let i = 0; i < 1000000; i++) {
+        assert(f(foo, thisValue, 20, 30) === thisValue);
+    }
+}
+{
+    let f = test2(foo, thisValue);
+    for (let i = 0; i < 1000000; i++) {
+        assert(f(foo, thisValue, 20, 30) === thisValue);
+    }
+}
+const verbose = false;
+if (verbose)
+    print(Date.now() - start);
diff --git a/JSTests/stress/bind-args.js b/JSTests/stress/bind-args.js
new file mode 100644 (file)
index 0000000..677d6fb
--- /dev/null
@@ -0,0 +1,34 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+noInline(shouldBe);
+
+function test(a, b, c, d, e)
+{
+    return a + b + c + d + e;
+}
+noInline(test);
+
+function test2(a, b, c, d, e, f)
+{
+    return a + b + c + d + e + f;
+}
+noInline(test2);
+
+for (var i = 0; i < 3e4; ++i) {
+    shouldBe(test.bind(undefined)(1, 2, 3, 4, 5), 15);
+    shouldBe(test.bind(undefined, 1)(2, 3, 4, 5), 15);
+    shouldBe(test.bind(undefined, 2, 3)(4, 5, 6), 20);
+    shouldBe(test.bind(undefined, 2, 3, 4)(5, 6), 20);
+    shouldBe(test.bind(undefined, 2, 3, 4, 5)(6), 20);
+    shouldBe(test.bind(undefined, 2, 3, 4, 5, 6)(), 20);
+
+    shouldBe(test2.bind(undefined)(1, 1, 2, 3, 4, 5), 16);
+    shouldBe(test2.bind(undefined, 1)(1, 2, 3, 4, 5), 16);
+    shouldBe(test2.bind(undefined, 1, 1)(2, 3, 4, 5), 16);
+    shouldBe(test2.bind(undefined, 1, 2, 3)(4, 5, 6), 21);
+    shouldBe(test2.bind(undefined, 1, 2, 3, 4)(5, 6), 21);
+    shouldBe(test2.bind(undefined, 1, 2, 3, 4, 5)(6), 21);
+    shouldBe(test2.bind(undefined, 1, 2, 3, 4, 5, 6)(), 21);
+}
index 29d742b..d543f00 100644 (file)
@@ -1,3 +1,14 @@
+2019-12-21  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Improve our bound function implementation
+        https://bugs.webkit.org/show_bug.cgi?id=205327
+
+        Reviewed by Keith Miller.
+
+        * inspector/model/remote-object-get-properties-expected.txt:
+        * inspector/runtime/getDisplayableProperties-expected.txt:
+        * inspector/runtime/getProperties-expected.txt:
+
 2019-12-21  Kate Cheney  <katherine_cheney@apple.com>
 
         Add timeStamp to ITP database
index d7c9ab6..8e0e2d7 100644 (file)
@@ -407,8 +407,8 @@ description: function unboundFunction() {
 }
 
 OWN PROPERTIES:
-    name
     length
+    name
     __proto__
     targetFunction
     boundThis
index d93a5c4..163379d 100644 (file)
@@ -33,8 +33,8 @@ Properties:
 Evaluating expression...
 Getting displayable properties...
 Properties:
-    "name"       =>  "bound Test" (string)  [configurable | isOwn]
     "length"     =>  0 (number)  [configurable | isOwn]
+    "name"       =>  "bound Test" (string)  [configurable | isOwn]
     "caller"     =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "arguments"  =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
@@ -46,8 +46,8 @@ Internal Properties:
 Evaluating expression...
 Getting displayable properties...
 Properties:
-    "name"       =>  "bound Test" (string)  [configurable | isOwn]
     "length"     =>  0 (number)  [configurable | isOwn]
+    "name"       =>  "bound Test" (string)  [configurable | isOwn]
     "caller"     =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "arguments"  =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
@@ -82,8 +82,8 @@ Properties:
 Evaluating expression...
 Getting displayable properties...
 Properties:
-    "name"       =>  "bound " (string)  [configurable | isOwn]
     "length"     =>  3 (number)  [configurable | isOwn]
+    "name"       =>  "bound " (string)  [configurable | isOwn]
     "caller"     =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "arguments"  =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
@@ -95,8 +95,8 @@ Internal Properties:
 Evaluating expression...
 Getting displayable properties...
 Properties:
-    "name"       =>  "bound " (string)  [configurable | isOwn]
     "length"     =>  0 (number)  [configurable | isOwn]
+    "name"       =>  "bound " (string)  [configurable | isOwn]
     "caller"     =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "arguments"  =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
@@ -109,8 +109,8 @@ Internal Properties:
 Evaluating expression...
 Getting displayable properties...
 Properties:
-    "name"       =>  "bound " (string)  [configurable | isOwn]
     "length"     =>  0 (number)  [configurable | isOwn]
+    "name"       =>  "bound " (string)  [configurable | isOwn]
     "caller"     =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "arguments"  =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
@@ -122,8 +122,8 @@ Internal Properties:
 Evaluating expression...
 Getting displayable properties...
 Properties:
-    "name"       =>  "bound " (string)  [configurable | isOwn]
     "length"     =>  0 (number)  [configurable | isOwn]
+    "name"       =>  "bound " (string)  [configurable | isOwn]
     "caller"     =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "arguments"  =>  "TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context." (object error)  [wasThrown]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
index 2180bd3..e0433ec 100644 (file)
@@ -31,8 +31,8 @@ Properties:
 Evaluating expression...
 Getting own properties...
 Properties:
-    "name"       =>  "bound Test" (string)  [configurable | isOwn]
     "length"     =>  0 (number)  [configurable | isOwn]
+    "name"       =>  "bound Test" (string)  [configurable | isOwn]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
 Internal Properties:
     "targetFunction"  =>  "class Test { }" (function class)  []
@@ -42,8 +42,8 @@ Internal Properties:
 Evaluating expression...
 Getting own properties...
 Properties:
-    "name"       =>  "bound Test" (string)  [configurable | isOwn]
     "length"     =>  0 (number)  [configurable | isOwn]
+    "name"       =>  "bound Test" (string)  [configurable | isOwn]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
 Internal Properties:
     "targetFunction"  =>  "class Test { }" (function class)  []
@@ -76,8 +76,8 @@ Properties:
 Evaluating expression...
 Getting own properties...
 Properties:
-    "name"       =>  "bound " (string)  [configurable | isOwn]
     "length"     =>  3 (number)  [configurable | isOwn]
+    "name"       =>  "bound " (string)  [configurable | isOwn]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
 Internal Properties:
     "targetFunction"  =>  "function (a, b, c){}" (function)  []
@@ -87,8 +87,8 @@ Internal Properties:
 Evaluating expression...
 Getting own properties...
 Properties:
-    "name"       =>  "bound " (string)  [configurable | isOwn]
     "length"     =>  0 (number)  [configurable | isOwn]
+    "name"       =>  "bound " (string)  [configurable | isOwn]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
 Internal Properties:
     "targetFunction"  =>  "function (a, b, c){}" (function)  []
@@ -99,8 +99,8 @@ Internal Properties:
 Evaluating expression...
 Getting own properties...
 Properties:
-    "name"       =>  "bound " (string)  [configurable | isOwn]
     "length"     =>  0 (number)  [configurable | isOwn]
+    "name"       =>  "bound " (string)  [configurable | isOwn]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
 Internal Properties:
     "targetFunction"  =>  "function (){}" (function)  []
@@ -110,8 +110,8 @@ Internal Properties:
 Evaluating expression...
 Getting own properties...
 Properties:
-    "name"       =>  "bound " (string)  [configurable | isOwn]
     "length"     =>  0 (number)  [configurable | isOwn]
+    "name"       =>  "bound " (string)  [configurable | isOwn]
     "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
 Internal Properties:
     "targetFunction"  =>  "function (){}" (function)  []
index 2fa4863..4bac5c6 100644 (file)
@@ -1,3 +1,233 @@
+2019-12-21  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Improve our bound function implementation
+        https://bugs.webkit.org/show_bug.cgi?id=205327
+
+        Reviewed by Keith Miller.
+
+        This patch improves Function#bind, and calling bound function with bound arguments.
+
+        1. Rename CallFrameSlot::argumentCount to CallFrameSlot::argumentCountIncludingThis.
+        2. Do not include name in NativeExecutable for JSBoundFunction. Putting name in NativeExecutable is assuming that function + name pair is almost identical.
+           This is true in host functions except for JSBoundFunction. JSBoundFunction should hold its name in JSBoundFunction.
+        3. Cache NativeExecutable for JSBoundFunction in the VM. We use a hash-map in JITThunk for NativeExecutables because we assume that host-function creation cannot be
+           done by the user program: each executable is pre-defined to exactly one object by the environment, and there is no way to create host-functions repeatedly from
+           the user-program. The only exception to this is JSBoundFunction so caching it on the VM avoids the hash-map lookup. This is not true for JSBoundFunction.
+        4. ThunkGenerator should support JSBoundFunction call with bound arguments. It turns out that Speedometer2/React-Redux-TodoMVC is using bound function with
+           bound arguments. Additionally, it is used. This is really bad: when dispatching an event, we first call this function from C++, entering JS world,
+           going back to C++ world again, and entering JS world to call bound function again. By using ThunkGenerator, we can eliminate this back and forth by directly
+           calling the bound JS Executable from the thunk. Previously, bound arguments are stored in JSArray. But it is difficult to access them from thunk since we need to consider
+           have-a-bad-time case. Instead, we use JSImmutableButterfly to save bound arguments so that JIT thunk can quickly access arguments. To capture arguments as
+           JSImmutableButterfly in JS world, we introduce op_create_arguments_butterfly, and handle it in all tiers.
+        5. It turns out that eager materialization of "length" in JSBoundFunction takes long time while it is rarely used. This patch makes length lazily reified for JSBoundFunction.
+        6. To make Function.prototype.bind faster, we track whether "name" and "length" properties of JSFunction is modified or not. This skips has-own-length-property check, which
+           makes Function.prototype.bind 11~% faster.
+
+        Combining things above, creation of JSBoundFunction is 80~% faster. And calling bound function with bound arguments is 3~x faster.
+        This improves Speedometer2/React-TodoMVC by ~3%.
+
+        * builtins/FunctionPrototype.js:
+        (bind):
+        * bytecode/AccessCase.cpp:
+        (JSC::AccessCase::generateImpl):
+        * bytecode/AccessCaseSnippetParams.cpp:
+        (JSC::SlowPathCallGeneratorWithArguments::generateImpl):
+        * bytecode/BytecodeIntrinsicRegistry.h:
+        * bytecode/BytecodeList.rb:
+        * bytecode/BytecodeUseDef.cpp:
+        (JSC::computeUsesForBytecodeIndexImpl):
+        (JSC::computeDefsForBytecodeIndexImpl):
+        * bytecode/VirtualRegister.cpp:
+        (JSC::VirtualRegister::dump const):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitCreateArgumentsButterfly):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::BytecodeIntrinsicNode::emit_intrinsic_createArgumentsButterfly):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGArgumentsEliminationPhase.cpp:
+        * dfg/DFGArgumentsUtilities.cpp:
+        (JSC::DFG::argumentsInvolveStackSlot):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::flushImpl):
+        (JSC::DFG::ByteCodeParser::handleVarargsInlining):
+        (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/DFGGraph.cpp:
+        (JSC::DFG::Graph::isLiveInBytecode):
+        * dfg/DFGGraph.h:
+        (JSC::DFG::Graph::forAllLocalsLiveInBytecode):
+        * dfg/DFGJITCompiler.cpp:
+        (JSC::DFG::JITCompiler::compileFunction):
+        * dfg/DFGJITCompiler.h:
+        (JSC::DFG::JITCompiler::emitStoreCallSiteIndex):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
+        (JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):
+        * dfg/DFGOSRExit.cpp:
+        (JSC::DFG::emitRestoreArguments):
+        (JSC::DFG::reifyInlinedCallFrames):
+        (JSC::DFG::OSRExit::emitRestoreArguments):
+        * dfg/DFGOSRExitCompilerCommon.cpp:
+        (JSC::DFG::reifyInlinedCallFrames):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPreciseLocalClobberize.h:
+        (JSC::DFG::PreciseLocalClobberizeAdaptor::readTop):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileCreateArgumentsButterfly):
+        (JSC::DFG::SpeculativeJIT::compileGetArgumentCountIncludingThis):
+        (JSC::DFG::SpeculativeJIT::compileSetArgumentCountIncludingThis):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::emitCall):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::emitCall):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStackLayoutPhase.cpp:
+        (JSC::DFG::StackLayoutPhase::run):
+        * dfg/DFGStoreBarrierInsertionPhase.cpp:
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLink.cpp:
+        (JSC::FTL::link):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::lower):
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCreateArgumentsButterfly):
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetArgumentCountIncludingThis):
+        (JSC::FTL::DFG::LowerDFGToB3::compileSetArgumentCountIncludingThis):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstruct):
+        (JSC::FTL::DFG::LowerDFGToB3::compileDirectCallOrConstruct):
+        (JSC::FTL::DFG::LowerDFGToB3::compileTailCall):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargsSpread):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargs):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallEval):
+        (JSC::FTL::DFG::LowerDFGToB3::getArgumentsLength):
+        (JSC::FTL::DFG::LowerDFGToB3::callPreflight):
+        * ftl/FTLSlowPathCall.h:
+        (JSC::FTL::callOperation):
+        * interpreter/CallFrame.cpp:
+        (JSC::CallFrame::callSiteAsRawBits const):
+        (JSC::CallFrame::unsafeCallSiteAsRawBits const):
+        (JSC::CallFrame::setCurrentVPC):
+        * interpreter/CallFrame.h:
+        (JSC::CallFrame::argumentCountIncludingThis const):
+        (JSC::CallFrame::setArgumentCountIncludingThis):
+        * jit/AssemblyHelpers.cpp:
+        (JSC::AssemblyHelpers::jitAssertArgumentCountSane):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::argumentCount):
+        * jit/CCallHelpers.h:
+        (JSC::CCallHelpers::prepareForTailCallSlow):
+        * jit/CallFrameShuffler.cpp:
+        (JSC::CallFrameShuffler::dump const):
+        (JSC::CallFrameShuffler::prepareForTailCall):
+        (JSC::CallFrameShuffler::prepareAny):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        (JSC::JIT::compileWithoutLinking):
+        * jit/JITCall.cpp:
+        (JSC::JIT::compileSetupFrame):
+        (JSC::JIT::compileOpCall):
+        * jit/JITCall32_64.cpp:
+        (JSC::JIT::compileSetupFrame):
+        (JSC::JIT::compileOpCall):
+        * jit/JITInlines.h:
+        (JSC::JIT::updateTopCallFrame):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_argument_count):
+        (JSC::JIT::emit_op_get_rest_length):
+        (JSC::JIT::emit_op_get_argument):
+        * jit/SetupVarargsFrame.cpp:
+        (JSC::emitSetupVarargsFrameFastCase):
+        * jit/SpecializedThunkJIT.h:
+        (JSC::SpecializedThunkJIT::SpecializedThunkJIT):
+        * jit/ThunkGenerators.cpp:
+        (JSC::arityFixupGenerator):
+        (JSC::boundFunctionCallGenerator):
+        (JSC::boundThisNoArgsFunctionCallGenerator): Deleted.
+        * jit/ThunkGenerators.h:
+        * jsc.cpp:
+        * llint/LLIntData.cpp:
+        (JSC::LLInt::Data::performAssertions):
+        * llint/LowLevelInterpreter.asm:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * llint/WebAssembly.asm:
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/CommonSlowPaths.h:
+        * runtime/ExecutableBase.h:
+        * runtime/FunctionRareData.cpp:
+        (JSC::FunctionRareData::FunctionRareData):
+        * runtime/FunctionRareData.h:
+        * runtime/IntlCollatorPrototype.cpp:
+        (JSC::IntlCollatorPrototypeGetterCompare):
+        * runtime/IntlDateTimeFormatPrototype.cpp:
+        (JSC::IntlDateTimeFormatPrototypeGetterFormat):
+        * runtime/IntlNumberFormatPrototype.cpp:
+        (JSC::IntlNumberFormatPrototypeGetterFormat):
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/JSBoundFunction.cpp:
+        (JSC::boundThisNoArgsFunctionCall):
+        (JSC::boundFunctionCall):
+        (JSC::boundThisNoArgsFunctionConstruct):
+        (JSC::boundFunctionConstruct):
+        (JSC::JSBoundFunction::create):
+        (JSC::JSBoundFunction::JSBoundFunction):
+        (JSC::JSBoundFunction::boundArgsCopy):
+        (JSC::JSBoundFunction::visitChildren):
+        * runtime/JSBoundFunction.h:
+        * runtime/JSFunction.cpp:
+        (JSC::JSFunction::finishCreation):
+        (JSC::JSFunction::name):
+        (JSC::JSFunction::getOwnPropertySlot):
+        (JSC::JSFunction::getOwnNonIndexPropertyNames):
+        (JSC::JSFunction::put):
+        (JSC::JSFunction::deleteProperty):
+        (JSC::JSFunction::defineOwnProperty):
+        (JSC::JSFunction::reifyLength):
+        (JSC::JSFunction::reifyLazyPropertyIfNeeded):
+        (JSC::JSFunction::reifyLazyPropertyForHostOrBuiltinIfNeeded):
+        (JSC::JSFunction::reifyLazyBoundNameIfNeeded):
+        * runtime/JSFunction.h:
+        * runtime/JSFunctionInlines.h:
+        (JSC::JSFunction::areNameAndLengthOriginal):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::makeBoundFunction):
+        (JSC::hasOwnLengthProperty):
+        * runtime/JSObject.h:
+        (JSC::getJSFunction):
+        (JSC::getCallData): Deleted.
+        (JSC::getConstructData): Deleted.
+        * runtime/JSObjectInlines.h:
+        (JSC::getCallData):
+        (JSC::getConstructData):
+        * runtime/VM.cpp:
+        (JSC::thunkGeneratorForIntrinsic):
+        (JSC::VM::getBoundFunction):
+        * runtime/VM.h:
+        * wasm/js/WasmToJS.cpp:
+        (JSC::Wasm::wasmToJS):
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::WebAssemblyFunction::jsCallEntrypointSlow):
+
 2019-12-20  Darin Adler  <darin@apple.com>
 
         Make JSString values from literals in a single consistent style
index e40a696..f593527 100644 (file)
@@ -61,23 +61,21 @@ function bind(thisValue)
 {
     "use strict";
 
-    let target = this;
+    var target = this;
     if (typeof target !== "function")
         @throwTypeError("|this| is not a function inside Function.prototype.bind");
 
-    let argumentCount = arguments.length;
-    let boundArgs = null;
-    let numBoundArgs = 0;
+    var argumentCount = @argumentCount();
+    var boundArgs = null;
+    var numBoundArgs = 0;
     if (argumentCount > 1) {
         numBoundArgs = argumentCount - 1;
-        boundArgs = @newArrayWithSize(numBoundArgs);
-        for (let i = 0; i < numBoundArgs; i++)
-            @putByValDirect(boundArgs, i, arguments[i + 1]);
+        boundArgs = @createArgumentsButterfly();
     }
 
-    let length = 0;
+    var length = 0;
     if (@hasOwnLengthProperty(target)) {
-        let lengthValue = target.length;
+        var lengthValue = target.length;
         if (typeof lengthValue === "number") {
             lengthValue = lengthValue | 0;
             // Note that we only care about positive lengthValues, however, this comparision
@@ -87,9 +85,9 @@ function bind(thisValue)
         }
     }
 
-    let name = target.name;
+    var name = target.name;
     if (typeof name !== "string")
         name = "";
 
-    return @makeBoundFunction(target, arguments[0], boundArgs, length, name);
+    return @makeBoundFunction(target, thisValue, boundArgs, length, name);
 }
index 60062f9..dd9acca 100644 (file)
@@ -1508,7 +1508,7 @@ void AccessCase::generateImpl(AccessGenerationState& state)
 
         jit.store32(
             CCallHelpers::TrustedImm32(state.callSiteIndexForExceptionHandlingOrOriginal().bits()),
-            CCallHelpers::tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCount)));
+            CCallHelpers::tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCountIncludingThis)));
 
         if (m_type == Getter || m_type == Setter) {
             auto& access = this->as<GetterSetterAccessCase>();
@@ -1585,7 +1585,7 @@ void AccessCase::generateImpl(AccessGenerationState& state)
 
             jit.store32(
                 CCallHelpers::TrustedImm32(numberOfParameters),
-                calleeFrame.withOffset(CallFrameSlot::argumentCount * sizeof(Register) + PayloadOffset));
+                calleeFrame.withOffset(CallFrameSlot::argumentCountIncludingThis * sizeof(Register) + PayloadOffset));
 
             jit.storeCell(
                 loadedValueGPR, calleeFrame.withOffset(CallFrameSlot::callee * sizeof(Register)));
@@ -1797,7 +1797,7 @@ void AccessCase::generateImpl(AccessGenerationState& state)
                 jit.store32(
                     CCallHelpers::TrustedImm32(
                         state.callSiteIndexForExceptionHandlingOrOriginal().bits()),
-                    CCallHelpers::tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCount)));
+                    CCallHelpers::tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCountIncludingThis)));
                 
                 jit.makeSpaceOnStackForCCall();
                 
index a174e24..b1d404a 100644 (file)
@@ -55,7 +55,7 @@ public:
 
         jit.store32(
             CCallHelpers::TrustedImm32(state.callSiteIndexForExceptionHandlingOrOriginal().bits()),
-            CCallHelpers::tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCount)));
+            CCallHelpers::tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCountIncludingThis)));
 
         jit.makeSpaceOnStackForCCall();
 
index f76f785..849e34b 100644 (file)
@@ -74,6 +74,7 @@ enum class LinkTimeConstant : int32_t;
     macro(newArrayWithSize) \
     macro(newPromise) \
     macro(createPromise) \
+    macro(createArgumentsButterfly) \
     macro(defineEnumerableWritableConfigurableDataProperty) \
 
 #define JSC_COMMON_BYTECODE_INTRINSIC_CONSTANTS_EACH_NAME(macro) \
index 71bf6b0..a9bd483 100644 (file)
@@ -108,6 +108,11 @@ op :create_cloned_arguments,
         dst: VirtualRegister,
     }
 
+op :create_arguments_butterfly,
+    args: {
+        dst: VirtualRegister,
+    }
+
 op :create_this,
     args: {
         dst: VirtualRegister,
index eb63293..c018ddf 100644 (file)
@@ -81,6 +81,7 @@ void computeUsesForBytecodeIndexImpl(VirtualRegister scopeRegister, const Instru
     case op_profile_control_flow:
     case op_create_direct_arguments:
     case op_create_cloned_arguments:
+    case op_create_arguments_butterfly:
     case op_get_rest_length:
     case op_check_traps:
     case op_get_argument:
@@ -471,6 +472,7 @@ void computeDefsForBytecodeIndexImpl(unsigned numVars, const Instruction* instru
     DEFS(OpCreateDirectArguments, dst)
     DEFS(OpCreateScopedArguments, dst)
     DEFS(OpCreateClonedArguments, dst)
+    DEFS(OpCreateArgumentsButterfly, dst)
     DEFS(OpDelById, dst)
     DEFS(OpDelByVal, dst)
     DEFS(OpUnsigned, dst)
index 45c6ba1..c62d951 100644 (file)
@@ -42,8 +42,8 @@ void VirtualRegister::dump(PrintStream& out) const
             out.print("codeBlock");
         else if (m_virtualRegister == CallFrameSlot::callee)
             out.print("callee");
-        else if (m_virtualRegister == CallFrameSlot::argumentCount)
-            out.print("argumentCount");
+        else if (m_virtualRegister == CallFrameSlot::argumentCountIncludingThis)
+            out.print("argumentCountIncludingThis");
 #if CPU(ADDRESS64)
         else if (!m_virtualRegister)
             out.print("callerFrame");
index f80a7c4..b52fc62 100644 (file)
@@ -2726,6 +2726,12 @@ RegisterID* BytecodeGenerator::emitCreateAsyncGenerator(RegisterID* dst, Registe
     return dst;
 }
 
+RegisterID* BytecodeGenerator::emitCreateArgumentsButterfly(RegisterID* dst)
+{
+    OpCreateArgumentsButterfly::emit(this, dst);
+    return dst;
+}
+
 void BytecodeGenerator::emitTDZCheck(RegisterID* target)
 {
     OpCheckTdz::emit(this, target);
index 93b13f6..e14aa16 100644 (file)
@@ -726,6 +726,7 @@ namespace JSC {
         RegisterID* emitCreatePromise(RegisterID* dst, RegisterID* newTarget, bool isInternalPromise);
         RegisterID* emitCreateGenerator(RegisterID* dst, RegisterID* newTarget);
         RegisterID* emitCreateAsyncGenerator(RegisterID* dst, RegisterID* newTarget);
+        RegisterID* emitCreateArgumentsButterfly(RegisterID* dst);
         void emitTDZCheck(RegisterID* target);
         bool needsTDZCheck(const Variable&);
         void emitTDZCheckIfNecessary(const Variable&, RegisterID* target, RegisterID* scope);
index 5a93013..e4f2aea 100644 (file)
@@ -1463,6 +1463,12 @@ RegisterID* BytecodeIntrinsicNode::emit_intrinsic_newPromise(JSC::BytecodeGenera
     return finalDestination.get();
 }
 
+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_createArgumentsButterfly(JSC::BytecodeGenerator& generator, JSC::RegisterID* dst)
+{
+    ASSERT(!m_args->m_listNode);
+    return generator.emitCreateArgumentsButterfly(generator.finalDestination(dst));
+}
+
 RegisterID* BytecodeIntrinsicNode::emit_intrinsic_defineEnumerableWritableConfigurableDataProperty(JSC::BytecodeGenerator& generator, JSC::RegisterID* dst)
 {
     ArgumentListNode* node = m_args->m_listNode;
index 64acb54..dc1c64e 100644 (file)
@@ -2964,6 +2964,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         setForNode(node, m_codeBlock->globalObjectFor(node->origin.semantic)->clonedArgumentsStructure());
         break;
 
+    case CreateArgumentsButterfly:
+        setForNode(node, m_vm.immutableButterflyStructures[arrayIndexFromIndexingType(CopyOnWriteArrayWithContiguous) - NumberOfIndexingShapes].get());
+        break;
+
     case NewGeneratorFunction:
         setForNode(node, 
             m_codeBlock->globalObjectFor(node->origin.semantic)->generatorFunctionStructure());
index af2eda4..713e799 100644 (file)
@@ -560,7 +560,7 @@ private:
                         if (inlineCallFrame) {
                             if (inlineCallFrame->isVarargs()) {
                                 isClobberedByBlock |= clobberedByThisBlock.operand(
-                                    inlineCallFrame->stackOffset + CallFrameSlot::argumentCount);
+                                    inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis);
                             }
 
                             if (!isClobberedByBlock || inlineCallFrame->isClosureCall) {
index 6261123..ff1d568 100644 (file)
@@ -42,7 +42,7 @@ bool argumentsInvolveStackSlot(InlineCallFrame* inlineCallFrame, VirtualRegister
         return true;
     
     if (inlineCallFrame->isVarargs()
-        && reg == VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount))
+        && reg == VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis))
         return true;
     
     // We do not include fixups here since it is not related to |arguments|, rest parameters, and varargs.
index 7e4a9fe..030fabf 100644 (file)
@@ -552,7 +552,7 @@ private:
             if (inlineCallFrame->isClosureCall)
                 addFlushDirect(inlineCallFrame, remapOperand(inlineCallFrame, VirtualRegister(CallFrameSlot::callee)));
             if (inlineCallFrame->isVarargs())
-                addFlushDirect(inlineCallFrame, remapOperand(inlineCallFrame, VirtualRegister(CallFrameSlot::argumentCount)));
+                addFlushDirect(inlineCallFrame, remapOperand(inlineCallFrame, VirtualRegister(CallFrameSlot::argumentCountIncludingThis)));
         } else
             numArguments = m_graph.baselineCodeBlockFor(inlineCallFrame)->numParameters();
 
@@ -1902,7 +1902,7 @@ bool ByteCodeParser::handleVarargsInlining(Node* callTargetNode, VirtualRegister
         
         LoadVarargsData* data = m_graph.m_loadVarargsData.add();
         data->start = VirtualRegister(remappedArgumentStart + 1);
-        data->count = VirtualRegister(remappedRegisterOffset + CallFrameSlot::argumentCount);
+        data->count = VirtualRegister(remappedRegisterOffset + CallFrameSlot::argumentCountIncludingThis);
         data->offset = argumentsOffset;
         data->limit = maxArgumentCountIncludingThis;
         data->mandatoryMinimum = mandatoryMinimum;
@@ -1922,7 +1922,7 @@ bool ByteCodeParser::handleVarargsInlining(Node* callTargetNode, VirtualRegister
         // SSA. Fortunately, we also have other reasons for not inserting control flow
         // before SSA.
         
-        VariableAccessData* countVariable = newVariableAccessData(VirtualRegister(remappedRegisterOffset + CallFrameSlot::argumentCount));
+        VariableAccessData* countVariable = newVariableAccessData(VirtualRegister(remappedRegisterOffset + CallFrameSlot::argumentCountIncludingThis));
         // This is pretty lame, but it will force the count to be flushed as an int. This doesn't
         // matter very much, since our use of a SetArgumentDefinitely and Flushes for this local slot is
         // mostly just a formality.
@@ -6897,6 +6897,13 @@ void ByteCodeParser::parseBlock(unsigned limit)
             set(bytecode.m_dst, createArguments);
             NEXT_OPCODE(op_create_cloned_arguments);
         }
+
+        case op_create_arguments_butterfly: {
+            auto bytecode = currentInstruction->as<OpCreateArgumentsButterfly>();
+            noticeArgumentsUse();
+            set(bytecode.m_dst, addToGraph(CreateArgumentsButterfly));
+            NEXT_OPCODE(op_create_arguments_butterfly);
+        }
             
         case op_get_from_arguments: {
             auto bytecode = currentInstruction->as<OpGetFromArguments>();
index fd0b6d7..2477af0 100644 (file)
@@ -234,6 +234,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, const I
     case op_create_direct_arguments:
     case op_create_scoped_arguments:
     case op_create_cloned_arguments:
+    case op_create_arguments_butterfly:
     case op_get_from_arguments:
     case op_put_to_arguments:
     case op_get_argument:
index 89bc641..5f02ac0 100644 (file)
@@ -113,7 +113,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         if (inlineCallFrame->isClosureCall)
             read(AbstractHeap(Stack, inlineCallFrame->stackOffset + CallFrameSlot::callee));
         if (inlineCallFrame->isVarargs())
-            read(AbstractHeap(Stack, inlineCallFrame->stackOffset + CallFrameSlot::argumentCount));
+            read(AbstractHeap(Stack, inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis));
     }
 
     // We don't want to specifically account which nodes can read from the scope
@@ -524,6 +524,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case CreateDirectArguments:
     case CreateScopedArguments:
     case CreateClonedArguments:
+    case CreateArgumentsButterfly:
         read(Stack);
         read(HeapObjectCount);
         write(HeapObjectCount);
@@ -773,14 +774,14 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         return;
         
     case GetArgumentCountIncludingThis: {
-        auto heap = AbstractHeap(Stack, remapOperand(node->argumentsInlineCallFrame(), VirtualRegister(CallFrameSlot::argumentCount)));
+        auto heap = AbstractHeap(Stack, remapOperand(node->argumentsInlineCallFrame(), VirtualRegister(CallFrameSlot::argumentCountIncludingThis)));
         read(heap);
         def(HeapLocation(StackPayloadLoc, heap), LazyNode(node));
         return;
     }
 
     case SetArgumentCountIncludingThis:
-        write(AbstractHeap(Stack, CallFrameSlot::argumentCount));
+        write(AbstractHeap(Stack, CallFrameSlot::argumentCountIncludingThis));
         return;
 
     case GetRestLength:
index 8541231..8b53943 100644 (file)
@@ -257,6 +257,7 @@ bool doesGC(Graph& graph, Node* node)
     case CreateDirectArguments:
     case CreateScopedArguments:
     case CreateClonedArguments:
+    case CreateArgumentsButterfly:
     case Call:
     case CallEval:
     case CallForwardVarargs:
index aee1f00..8ab88cc 100644 (file)
@@ -2521,6 +2521,7 @@ private:
         case FilterPutByIdStatus:
         case FilterInByIdStatus:
         case InvalidationPoint:
+        case CreateArgumentsButterfly:
             break;
 #else
         default:
index b320f70..7d0fa36 100644 (file)
@@ -1162,7 +1162,7 @@ bool Graph::isLiveInBytecode(VirtualRegister operand, CodeOrigin codeOrigin)
                 }
                 
                 if (inlineCallFrame->isVarargs()
-                    && reg.offset() == CallFrameSlot::argumentCount) {
+                    && reg.offset() == CallFrameSlot::argumentCountIncludingThis) {
                     if (verbose)
                         dataLog("Looks like the argument count.\n");
                     return true;
index fab9cf0..ec4f22b 100644 (file)
@@ -865,7 +865,7 @@ public:
                 if (inlineCallFrame->isClosureCall)
                     functor(stackOffset + CallFrameSlot::callee);
                 if (inlineCallFrame->isVarargs())
-                    functor(stackOffset + CallFrameSlot::argumentCount);
+                    functor(stackOffset + CallFrameSlot::argumentCountIncludingThis);
             }
             
             CodeBlock* codeBlock = baselineCodeBlockFor(inlineCallFrame);
index 3d843ce..70eef88 100644 (file)
@@ -470,7 +470,7 @@ void JITCompiler::compileFunction()
         arityCheck = label();
         compileEntry();
 
-        load32(AssemblyHelpers::payloadFor((VirtualRegister)CallFrameSlot::argumentCount), GPRInfo::regT1);
+        load32(AssemblyHelpers::payloadFor((VirtualRegister)CallFrameSlot::argumentCountIncludingThis), GPRInfo::regT1);
         branch32(AboveOrEqual, GPRInfo::regT1, TrustedImm32(m_codeBlock->numParameters())).linkTo(fromArityCheck, this);
         emitStoreCodeOrigin(CodeOrigin(BytecodeIndex(0)));
         if (maxFrameExtentForSlowPathCall)
index 68ca98d..d1bd7da 100644 (file)
@@ -135,7 +135,7 @@ public:
 
     void emitStoreCallSiteIndex(CallSiteIndex callSite)
     {
-        store32(TrustedImm32(callSite.bits()), tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCount)));
+        store32(TrustedImm32(callSite.bits()), tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCountIncludingThis)));
     }
 
     // Add a call out from JIT code, without an exception check.
index 24c733d..81913ed 100644 (file)
@@ -426,6 +426,7 @@ namespace JSC { namespace DFG {
     macro(CreateScopedArguments, NodeResultJS) \
     macro(CreateClonedArguments, NodeResultJS) \
     macro(PhantomClonedArguments, NodeResultJS | NodeMustGenerate) \
+    macro(CreateArgumentsButterfly, NodeResultJS) \
     macro(GetFromArguments, NodeResultJS) \
     macro(PutToArguments, NodeMustGenerate) \
     macro(GetArgument, NodeResultJS) \
index 3280f87..79bc796 100644 (file)
@@ -272,7 +272,7 @@ void LocalOSRAvailabilityCalculator::executeNode(Node* node)
         if (inlineCallFrame->isVarargs()) {
             // Record how to read each argument and the argument count.
             Availability argumentCount =
-                m_availability.m_locals.operand(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount);
+                m_availability.m_locals.operand(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis);
             
             m_availability.m_heap.set(PromotedHeapLocation(ArgumentCountPLoc, node), argumentCount);
         }
index 18c6414..6fedb19 100644 (file)
@@ -295,7 +295,7 @@ static void emitRestoreArguments(Context& context, CodeBlock* codeBlock, DFG::JI
 
         int32_t argumentCount;
         if (!inlineCallFrame || inlineCallFrame->isVarargs())
-            argumentCount = frame.operand<int32_t>(stackOffset + CallFrameSlot::argumentCount, PayloadOffset);
+            argumentCount = frame.operand<int32_t>(stackOffset + CallFrameSlot::argumentCountIncludingThis, PayloadOffset);
         else
             argumentCount = inlineCallFrame->argumentCountIncludingThis;
 
@@ -815,18 +815,18 @@ static void reifyInlinedCallFrames(Context& context, CodeBlock* outermostBaselin
         }
 
         if (!inlineCallFrame->isVarargs())
-            frame.setOperand<uint32_t>(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount, PayloadOffset, inlineCallFrame->argumentCountIncludingThis);
+            frame.setOperand<uint32_t>(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis, PayloadOffset, inlineCallFrame->argumentCountIncludingThis);
         ASSERT(callerFrame);
         frame.set<void*>(inlineCallFrame->callerFrameOffset(), callerFrame);
 #if USE(JSVALUE64)
         uint32_t locationBits = CallSiteIndex(codeOrigin->bytecodeIndex()).bits();
-        frame.setOperand<uint32_t>(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount, TagOffset, locationBits);
+        frame.setOperand<uint32_t>(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis, TagOffset, locationBits);
         if (!inlineCallFrame->isClosureCall)
             frame.setOperand(inlineCallFrame->stackOffset + CallFrameSlot::callee, JSValue(inlineCallFrame->calleeConstant()));
 #else // USE(JSVALUE64) // so this is the 32-bit part
         const Instruction* instruction = baselineCodeBlock->instructions().at(codeOrigin->bytecodeIndex()).ptr();
         uint32_t locationBits = CallSiteIndex(BytecodeIndex(bitwise_cast<uint32_t>(instruction))).bits();
-        frame.setOperand<uint32_t>(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount, TagOffset, locationBits);
+        frame.setOperand<uint32_t>(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis, TagOffset, locationBits);
         frame.setOperand<uint32_t>(inlineCallFrame->stackOffset + CallFrameSlot::callee, TagOffset, static_cast<uint32_t>(JSValue::CellTag));
         if (!inlineCallFrame->isClosureCall)
             frame.setOperand(inlineCallFrame->stackOffset + CallFrameSlot::callee, PayloadOffset, inlineCallFrame->calleeConstant());
@@ -841,7 +841,7 @@ static void reifyInlinedCallFrames(Context& context, CodeBlock* outermostBaselin
         const Instruction* instruction = outermostBaselineCodeBlock->instructions().at(codeOrigin->bytecodeIndex()).ptr();
         uint32_t locationBits = CallSiteIndex(BytecodeIndex(bitwise_cast<uint32_t>(instruction))).bits();
 #endif
-        frame.setOperand<uint32_t>(CallFrameSlot::argumentCount, TagOffset, locationBits);
+        frame.setOperand<uint32_t>(CallFrameSlot::argumentCountIncludingThis, TagOffset, locationBits);
     }
 }
 
@@ -997,7 +997,7 @@ void OSRExit::emitRestoreArguments(CCallHelpers& jit, VM& vm, const Operands<Val
 
         if (!inlineCallFrame || inlineCallFrame->isVarargs()) {
             jit.load32(
-                AssemblyHelpers::payloadFor(stackOffset + CallFrameSlot::argumentCount),
+                AssemblyHelpers::payloadFor(stackOffset + CallFrameSlot::argumentCountIncludingThis),
                 GPRInfo::regT1);
         } else {
             jit.move(
index 2ff3ea8..9b021bb 100644 (file)
@@ -305,18 +305,18 @@ void reifyInlinedCallFrames(CCallHelpers& jit, const OSRExitBase& exit)
         }
 
         if (!inlineCallFrame->isVarargs())
-            jit.store32(AssemblyHelpers::TrustedImm32(inlineCallFrame->argumentCountIncludingThis), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount)));
+            jit.store32(AssemblyHelpers::TrustedImm32(inlineCallFrame->argumentCountIncludingThis), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis)));
 #if USE(JSVALUE64)
         jit.storePtr(callerFrameGPR, AssemblyHelpers::addressForByteOffset(inlineCallFrame->callerFrameOffset()));
         uint32_t locationBits = CallSiteIndex(codeOrigin->bytecodeIndex()).bits();
-        jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount)));
+        jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis)));
         if (!inlineCallFrame->isClosureCall)
             jit.store64(AssemblyHelpers::TrustedImm64(JSValue::encode(JSValue(inlineCallFrame->calleeConstant()))), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + CallFrameSlot::callee)));
 #else // USE(JSVALUE64) // so this is the 32-bit part
         jit.storePtr(callerFrameGPR, AssemblyHelpers::addressForByteOffset(inlineCallFrame->callerFrameOffset()));
         const Instruction* instruction = baselineCodeBlock->instructions().at(codeOrigin->bytecodeIndex()).ptr();
         uint32_t locationBits = CallSiteIndex(BytecodeIndex(bitwise_cast<uint32_t>(instruction))).bits();
-        jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount)));
+        jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis)));
         jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + CallFrameSlot::callee)));
         if (!inlineCallFrame->isClosureCall)
             jit.storePtr(AssemblyHelpers::TrustedImmPtr(inlineCallFrame->calleeConstant()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + CallFrameSlot::callee)));
@@ -331,7 +331,7 @@ void reifyInlinedCallFrames(CCallHelpers& jit, const OSRExitBase& exit)
         const Instruction* instruction = jit.baselineCodeBlock()->instructions().at(codeOrigin->bytecodeIndex()).ptr();
         uint32_t locationBits = CallSiteIndex(BytecodeIndex(bitwise_cast<uint32_t>(instruction))).bits();
 #endif
-        jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(CallFrameSlot::argumentCount)));
+        jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(CallFrameSlot::argumentCountIncludingThis)));
     }
 }
 
index 50ca00b..b4a080d 100644 (file)
@@ -2134,6 +2134,23 @@ JSCell* JIT_OPERATION operationCreateClonedArguments(JSGlobalObject* globalObjec
         globalObject, structure, argumentStart, length, callee);
 }
 
+JSCell* JIT_OPERATION operationCreateArgumentsButterfly(JSGlobalObject* globalObject, Register* argumentStart, uint32_t argumentCount)
+{
+    VM& vm = globalObject->vm();
+    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSImmutableButterfly* butterfly = JSImmutableButterfly::tryCreate(vm, vm.immutableButterflyStructures[arrayIndexFromIndexingType(CopyOnWriteArrayWithContiguous) - NumberOfIndexingShapes].get(), argumentCount);
+    if (!butterfly) {
+        throwOutOfMemoryError(globalObject, scope);
+        return nullptr;
+    }
+    for (unsigned index = 0; index < argumentCount; ++index)
+        butterfly->setIndex(vm, index, argumentStart[index].jsValue());
+    return butterfly;
+}
+
 JSCell* JIT_OPERATION operationCreateDirectArgumentsDuringExit(VM* vmPointer, InlineCallFrame* inlineCallFrame, JSFunction* callee, uint32_t argumentCount)
 {
     VM& vm = *vmPointer;
index 7d24d06..339f918 100644 (file)
@@ -197,6 +197,7 @@ JSCell* JIT_OPERATION operationCreateDirectArgumentsDuringExit(VM*, InlineCallFr
 JSCell* JIT_OPERATION operationCreateScopedArguments(JSGlobalObject*, Structure*, Register* argumentStart, uint32_t length, JSFunction* callee, JSLexicalEnvironment*);
 JSCell* JIT_OPERATION operationCreateClonedArgumentsDuringExit(VM*, InlineCallFrame*, JSFunction*, uint32_t argumentCount);
 JSCell* JIT_OPERATION operationCreateClonedArguments(JSGlobalObject*, Structure*, Register* argumentStart, uint32_t length, JSFunction* callee);
+JSCell* JIT_OPERATION operationCreateArgumentsButterfly(JSGlobalObject*, Register* argumentStart, uint32_t argumentCount);
 JSCell* JIT_OPERATION operationCreateRest(JSGlobalObject*, Register* argumentStart, unsigned numberOfArgumentsToSkip, unsigned arraySize);
 JSCell* JIT_OPERATION operationNewArrayBuffer(VM*, Structure*, JSCell*) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationSetAdd(JSGlobalObject*, JSCell*, EncodedJSValue, int32_t) WTF_INTERNAL;
index a58e179..128c841 100644 (file)
@@ -110,14 +110,14 @@ private:
                 // Read the outermost arguments and argument count.
                 for (unsigned i = numberOfArgumentsToSkip; i < static_cast<unsigned>(m_graph.m_codeBlock->numParameters()); i++)
                     m_read(virtualRegisterForArgument(i));
-                m_read(VirtualRegister(CallFrameSlot::argumentCount));
+                m_read(VirtualRegister(CallFrameSlot::argumentCountIncludingThis));
                 return;
             }
             
             for (unsigned i = numberOfArgumentsToSkip; i < inlineCallFrame->argumentsWithFixup.size(); i++)
                 m_read(VirtualRegister(inlineCallFrame->stackOffset + virtualRegisterForArgument(i).offset()));
             if (inlineCallFrame->isVarargs())
-                m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount));
+                m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis));
         };
 
         auto readSpread = [&] (Node* spread) {
@@ -158,6 +158,7 @@ private:
         case CreateDirectArguments:
         case CreateScopedArguments:
         case CreateClonedArguments:
+        case CreateArgumentsButterfly:
         case PhantomDirectArguments:
         case PhantomClonedArguments:
         case GetRestLength:
@@ -225,14 +226,14 @@ private:
             if (!inlineCallFrame) {
                 if (indexIncludingThis < static_cast<unsigned>(m_graph.m_codeBlock->numParameters()))
                     m_read(virtualRegisterForArgument(indexIncludingThis));
-                m_read(VirtualRegister(CallFrameSlot::argumentCount));
+                m_read(VirtualRegister(CallFrameSlot::argumentCountIncludingThis));
                 break;
             }
 
             ASSERT_WITH_MESSAGE(inlineCallFrame->isVarargs(), "GetArgument is only used for InlineCallFrame if the call frame is varargs.");
             if (indexIncludingThis < inlineCallFrame->argumentsWithFixup.size())
                 m_read(VirtualRegister(inlineCallFrame->stackOffset + virtualRegisterForArgument(indexIncludingThis).offset()));
-            m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount));
+            m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis));
             break;
         }
             
@@ -256,7 +257,7 @@ private:
                 if (inlineCallFrame->isClosureCall)
                     m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::callee));
                 if (inlineCallFrame->isVarargs())
-                    m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount));
+                    m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis));
             }
             break;
         } }
index 8050516..ece9470 100644 (file)
@@ -1125,6 +1125,11 @@ private:
             setPrediction(SpecObjectOther);
             break;
         }
+
+        case CreateArgumentsButterfly: {
+            setPrediction(SpecCellOther);
+            break;
+        }
             
         case FiatInt52: {
             RELEASE_ASSERT(enableInt52());
index 15be42a..b9a46be 100644 (file)
@@ -377,6 +377,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case CreateDirectArguments:
     case CreateScopedArguments:
     case CreateClonedArguments:
+    case CreateArgumentsButterfly:
     case GetFromArguments:
     case GetArgument:
     case PutToArguments:
index 78d4380..08b3ae7 100644 (file)
@@ -7808,6 +7808,28 @@ void SpeculativeJIT::compileCreateClonedArguments(Node* node)
     cellResult(resultGPR, node);
 }
 
+void SpeculativeJIT::compileCreateArgumentsButterfly(Node* node)
+{
+    GPRFlushedCallResult result(this);
+    GPRReg resultGPR = result.gpr();
+    flushRegisters();
+
+    JSGlobalObject* globalObject = m_jit.globalObjectFor(node->origin.semantic);
+
+    // We set up the arguments ourselves, because we have the whole register file and we can
+    // set them up directly into the argument registers.
+
+    // Arguments: 0:JSGlobalObject*, 1:start, 3:length
+    m_jit.setupArgument(2, [&] (GPRReg destGPR) { emitGetLength(node->origin.semantic, destGPR); });
+    m_jit.setupArgument(1, [&] (GPRReg destGPR) { emitGetArgumentStart(node->origin.semantic, destGPR); });
+    m_jit.setupArgument(0, [&] (GPRReg destGPR) { m_jit.move(TrustedImmPtr::weakPointer(m_graph, globalObject), destGPR); });
+
+    appendCallSetResult(operationCreateArgumentsButterfly, resultGPR);
+    m_jit.exceptionCheck();
+
+    cellResult(resultGPR, node);
+}
+
 void SpeculativeJIT::compileCreateRest(Node* node)
 {
     ASSERT(node->op() == CreateRest);
@@ -12446,14 +12468,14 @@ void SpeculativeJIT::compileGetArgumentCountIncludingThis(Node* node)
     if (InlineCallFrame* inlineCallFrame = node->argumentsInlineCallFrame())
         argumentCountRegister = inlineCallFrame->argumentCountRegister;
     else
-        argumentCountRegister = VirtualRegister(CallFrameSlot::argumentCount);
+        argumentCountRegister = VirtualRegister(CallFrameSlot::argumentCountIncludingThis);
     m_jit.load32(JITCompiler::payloadFor(argumentCountRegister), result.gpr());
     int32Result(result.gpr(), node);
 }
 
 void SpeculativeJIT::compileSetArgumentCountIncludingThis(Node* node)
 {
-    m_jit.store32(TrustedImm32(node->argumentCountIncludingThis()), JITCompiler::payloadFor(CallFrameSlot::argumentCount));
+    m_jit.store32(TrustedImm32(node->argumentCountIncludingThis()), JITCompiler::payloadFor(CallFrameSlot::argumentCountIncludingThis));
     noResult(node);
 }
 
index c64f978..22ad46f 100644 (file)
@@ -1401,6 +1401,7 @@ public:
     void compileGetArgument(Node*);
     void compileCreateScopedArguments(Node*);
     void compileCreateClonedArguments(Node*);
+    void compileCreateArgumentsButterfly(Node*);
     void compileCreateRest(Node*);
     void compileSpread(Node*);
     void compileNewArray(Node*);
index 67b0e0b..8c856c8 100644 (file)
@@ -735,7 +735,7 @@ void SpeculativeJIT::emitCall(Node* node)
             for (unsigned i = numPassedArgs; i < numAllocatedArgs; ++i)
                 shuffleData.args[i] = ValueRecovery::constant(jsUndefined());
         } else {
-            m_jit.store32(MacroAssembler::TrustedImm32(numPassedArgs), m_jit.calleeFramePayloadSlot(CallFrameSlot::argumentCount));
+            m_jit.store32(MacroAssembler::TrustedImm32(numPassedArgs), m_jit.calleeFramePayloadSlot(CallFrameSlot::argumentCountIncludingThis));
         
             for (unsigned i = 0; i < numPassedArgs; i++) {
                 Edge argEdge = m_jit.graph().m_varArgChildren[node->firstChild() + 1 + i];
@@ -3874,6 +3874,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case CreateArgumentsButterfly: {
+        compileCreateArgumentsButterfly(node);
+        break;
+    }
+
     case CreateRest: {
         compileCreateRest(node);
         break;
index 3e638c9..177a813 100644 (file)
@@ -693,7 +693,7 @@ void SpeculativeJIT::emitCall(Node* node)
 
             shuffleData.setupCalleeSaveRegisters(m_jit.codeBlock());
         } else {
-            m_jit.store32(MacroAssembler::TrustedImm32(numPassedArgs), JITCompiler::calleeFramePayloadSlot(CallFrameSlot::argumentCount));
+            m_jit.store32(MacroAssembler::TrustedImm32(numPassedArgs), JITCompiler::calleeFramePayloadSlot(CallFrameSlot::argumentCountIncludingThis));
 
             for (unsigned i = 0; i < numPassedArgs; i++) {
                 Edge argEdge = m_jit.graph().m_varArgChildren[node->firstChild() + 1 + i];
@@ -4513,6 +4513,12 @@ void SpeculativeJIT::compile(Node* node)
         compileCreateClonedArguments(node);
         break;
     }
+
+    case CreateArgumentsButterfly: {
+        compileCreateArgumentsButterfly(node);
+        break;
+    }
+
     case CreateRest: {
         compileCreateRest(node);
         break;
index beec86a..168b3df 100644 (file)
@@ -109,7 +109,7 @@ public:
             
             if (inlineCallFrame->isVarargs()) {
                 usedLocals.set(VirtualRegister(
-                    CallFrameSlot::argumentCount + inlineCallFrame->stackOffset).toLocal());
+                    CallFrameSlot::argumentCountIncludingThis + inlineCallFrame->stackOffset).toLocal());
             }
             
             for (unsigned argument = inlineCallFrame->argumentsWithFixup.size(); argument--;) {
@@ -175,7 +175,7 @@ public:
             
             if (inlineCallFrame->isVarargs()) {
                 inlineCallFrame->argumentCountRegister = assign(
-                    allocation, VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCount));
+                    allocation, VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis));
             }
             
             for (unsigned argument = inlineCallFrame->argumentsWithFixup.size(); argument--;) {
index 75867bf..69e68cc 100644 (file)
@@ -338,6 +338,7 @@ private:
             case CreateDirectArguments:
             case CreateScopedArguments:
             case CreateClonedArguments:
+            case CreateArgumentsButterfly:
             case NewFunction:
             case NewGeneratorFunction:
             case NewAsyncGeneratorFunction:
index 62c1557..64f2d16 100644 (file)
@@ -83,8 +83,6 @@ namespace JSC { namespace FTL {
     macro(FunctionRareData_internalFunctionAllocationProfile_structure, FunctionRareData::offsetOfInternalFunctionAllocationProfile() + InternalFunctionAllocationProfile::offsetOfStructure()) \
     macro(FunctionRareData_boundFunctionStructure, FunctionRareData::offsetOfBoundFunctionStructure()) \
     macro(FunctionRareData_allocationProfileClearingWatchpoint, FunctionRareData::offsetOfAllocationProfileClearingWatchpoint()) \
-    macro(FunctionRareData_hasReifiedLength, FunctionRareData::offsetOfHasReifiedLength()) \
-    macro(FunctionRareData_hasReifiedName, FunctionRareData::offsetOfHasReifiedName()) \
     macro(GetterSetter_getter, GetterSetter::offsetOfGetter()) \
     macro(GetterSetter_setter, GetterSetter::offsetOfSetter()) \
     macro(JSArrayBufferView_length, JSArrayBufferView::offsetOfLength()) \
index 781b71e..c151f21 100644 (file)
@@ -152,6 +152,7 @@ inline CapabilityLevel canCompile(Node* node)
     case CreateDirectArguments:
     case CreateScopedArguments:
     case CreateClonedArguments:
+    case CreateArgumentsButterfly:
     case GetFromArguments:
     case PutToArguments:
     case GetArgument:
index d487318..e307b09 100644 (file)
@@ -133,7 +133,7 @@ void link(State& state)
             CCallHelpers::JumpList mainPathJumps;
     
             jit.load32(
-                frame.withOffset(sizeof(Register) * CallFrameSlot::argumentCount),
+                frame.withOffset(sizeof(Register) * CallFrameSlot::argumentCountIncludingThis),
                 GPRInfo::regT1);
             mainPathJumps.append(jit.branch32(
                                      CCallHelpers::AboveOrEqual, GPRInfo::regT1,
index 684330a..0e4b34a 100644 (file)
@@ -312,7 +312,7 @@ public:
                     
                     jit.store32(
                         MacroAssembler::TrustedImm32(callSiteIndex.bits()),
-                        CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCount)));
+                        CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCountIncludingThis)));
                     jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm->topEntryFrame);
 
                     jit.move(CCallHelpers::TrustedImmPtr(jit.codeBlock()), GPRInfo::argumentGPR0);
@@ -1046,6 +1046,9 @@ private:
         case CreateClonedArguments:
             compileCreateClonedArguments();
             break;
+        case CreateArgumentsButterfly:
+            compileCreateArgumentsButterfly();
+            break;
         case ObjectCreate:
             compileObjectCreate();
             break;
@@ -5935,6 +5938,13 @@ private:
         setJSValue(result);
     }
 
+    void compileCreateArgumentsButterfly()
+    {
+        JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+        LValue result = vmCall(Int64, operationCreateArgumentsButterfly, weakPointer(globalObject), getArgumentsStart(), getArgumentsLength().value);
+        setJSValue(result);
+    }
+
     void compileCreateRest()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
@@ -7940,13 +7950,13 @@ private:
         if (InlineCallFrame* inlineCallFrame = m_node->argumentsInlineCallFrame())
             argumentCountRegister = inlineCallFrame->argumentCountRegister;
         else
-            argumentCountRegister = VirtualRegister(CallFrameSlot::argumentCount);
+            argumentCountRegister = VirtualRegister(CallFrameSlot::argumentCountIncludingThis);
         setInt32(m_out.load32(payloadFor(argumentCountRegister)));
     }
 
     void compileSetArgumentCountIncludingThis()
     {
-        m_out.store32(m_out.constInt32(m_node->argumentCountIncludingThis()), payloadFor(CallFrameSlot::argumentCount));
+        m_out.store32(m_out.constInt32(m_node->argumentCountIncludingThis()), payloadFor(CallFrameSlot::argumentCountIncludingThis));
     }
     
     void compileGetScope()
@@ -8468,7 +8478,7 @@ private:
         };
 
         addArgument(jsCallee, VirtualRegister(CallFrameSlot::callee), 0);
-        addArgument(m_out.constInt32(numArgs), VirtualRegister(CallFrameSlot::argumentCount), PayloadOffset);
+        addArgument(m_out.constInt32(numArgs), VirtualRegister(CallFrameSlot::argumentCountIncludingThis), PayloadOffset);
         for (unsigned i = 0; i < numArgs; ++i)
             addArgument(lowJSValue(m_graph.varArgChild(node, 1 + i)), virtualRegisterForArgument(i), 0);
 
@@ -8496,7 +8506,7 @@ private:
 
                 jit.store32(
                     CCallHelpers::TrustedImm32(callSiteIndex.bits()),
-                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCount)));
+                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCountIncludingThis)));
 
                 CallLinkInfo* callLinkInfo = jit.codeBlock()->addCallLinkInfo();
 
@@ -8578,7 +8588,7 @@ private:
             };
             
             addArgument(jsCallee, VirtualRegister(CallFrameSlot::callee), 0);
-            addArgument(m_out.constInt32(numPassedArgs), VirtualRegister(CallFrameSlot::argumentCount), PayloadOffset);
+            addArgument(m_out.constInt32(numPassedArgs), VirtualRegister(CallFrameSlot::argumentCountIncludingThis), PayloadOffset);
             for (unsigned i = 0; i < numPassedArgs; ++i)
                 addArgument(lowJSValue(m_graph.varArgChild(node, 1 + i)), virtualRegisterForArgument(i), 0);
             for (unsigned i = numPassedArgs; i < numAllocatedArgs; ++i)
@@ -8646,7 +8656,7 @@ private:
                     
                     jit.store32(
                         CCallHelpers::TrustedImm32(callSiteIndex.bits()),
-                        CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCount)));
+                        CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCountIncludingThis)));
                 
                     callLinkInfo->setFrameShuffleData(shuffleData);
                     CallFrameShuffler(jit, shuffleData).prepareForTailCall();
@@ -8689,7 +8699,7 @@ private:
 
                 jit.store32(
                     CCallHelpers::TrustedImm32(callSiteIndex.bits()),
-                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCount)));
+                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCountIncludingThis)));
                 
                 CCallHelpers::Call call = jit.nearCall();
                 jit.addPtr(
@@ -8805,7 +8815,7 @@ private:
                 // with the call site index of our frame. Bad things happen if it's not set.
                 jit.store32(
                     CCallHelpers::TrustedImm32(callSiteIndex.bits()),
-                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCount)));
+                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCountIncludingThis)));
 
                 CallFrameShuffleData shuffleData;
                 shuffleData.numLocals = state->jitCode->common.frameRegisterCount;
@@ -8951,7 +8961,7 @@ private:
 
                 jit.store32(
                     CCallHelpers::TrustedImm32(callSiteIndex.bits()),
-                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCount)));
+                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCountIncludingThis)));
 
                 CallLinkInfo* callLinkInfo = jit.codeBlock()->addCallLinkInfo();
 
@@ -9015,7 +9025,7 @@ private:
                     // Before touching stack values, we should update the stack pointer to protect them from signal stack.
                     jit.addPtr(CCallHelpers::TrustedImm32(sizeof(CallerFrameAndPC)), scratchGPR1, CCallHelpers::stackPointerRegister);
 
-                    jit.store32(scratchGPR2, CCallHelpers::Address(scratchGPR1, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset));
+                    jit.store32(scratchGPR2, CCallHelpers::Address(scratchGPR1, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset));
 
                     int storeOffset = CallFrame::thisArgumentOffset() * static_cast<int>(sizeof(Register));
 
@@ -9253,7 +9263,7 @@ private:
 
                 jit.store32(
                     CCallHelpers::TrustedImm32(callSiteIndex.bits()),
-                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCount)));
+                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCountIncludingThis)));
 
                 CallLinkInfo* callLinkInfo = jit.codeBlock()->addCallLinkInfo();
                 CallVarargsData* data = node->callVarargsData();
@@ -9476,7 +9486,7 @@ private:
         };
         
         addArgument(jsCallee, VirtualRegister(CallFrameSlot::callee), 0);
-        addArgument(m_out.constInt32(numArgs), VirtualRegister(CallFrameSlot::argumentCount), PayloadOffset);
+        addArgument(m_out.constInt32(numArgs), VirtualRegister(CallFrameSlot::argumentCountIncludingThis), PayloadOffset);
         for (unsigned i = 0; i < numArgs; ++i)
             addArgument(lowJSValue(m_graph.varArgChild(node, 1 + i)), virtualRegisterForArgument(i), 0);
         
@@ -9506,7 +9516,7 @@ private:
                 
                 jit.store32(
                     CCallHelpers::TrustedImm32(callSiteIndex.bits()),
-                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCount)));
+                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCountIncludingThis)));
                 
                 CallLinkInfo* callLinkInfo = jit.codeBlock()->addCallLinkInfo();
                 callLinkInfo->setUpCall(CallLinkInfo::Call, node->origin.semantic, GPRInfo::regT0);
@@ -12324,7 +12334,7 @@ private:
             
             VirtualRegister argumentCountRegister;
             if (!inlineCallFrame)
-                argumentCountRegister = VirtualRegister(CallFrameSlot::argumentCount);
+                argumentCountRegister = VirtualRegister(CallFrameSlot::argumentCountIncludingThis);
             else
                 argumentCountRegister = inlineCallFrame->argumentCountRegister;
             length.value = m_out.sub(m_out.load32(payloadFor(argumentCountRegister)), m_out.int32One);
@@ -17610,7 +17620,7 @@ private:
         CallSiteIndex callSiteIndex = m_ftlState.jitCode->common.addCodeOrigin(codeOrigin);
         m_out.store32(
             m_out.constInt32(callSiteIndex.bits()),
-            tagFor(CallFrameSlot::argumentCount));
+            tagFor(CallFrameSlot::argumentCountIncludingThis));
 #if !USE(BUILTIN_FRAME_ADDRESS) || !ASSERT_DISABLED
         m_out.storePtr(m_callFrame, m_out.absolute(&vm().topCallFrame));
 #endif
index 6934c3f..fe2a4ec 100644 (file)
@@ -100,7 +100,7 @@ SlowPathCall callOperation(
     if (callSiteIndex) {
         jit.store32(
             CCallHelpers::TrustedImm32(callSiteIndex.bits()),
-            CCallHelpers::tagFor(CallFrameSlot::argumentCount));
+            CCallHelpers::tagFor(CallFrameSlot::argumentCountIncludingThis));
     }
     return callOperation(vm, usedRegisters, jit, exceptionTarget, function, resultGPR, arguments...);
 }
index f6394ac..1a4de3f 100644 (file)
@@ -90,12 +90,12 @@ bool CallFrame::callSiteBitsAreCodeOriginIndex() const
 
 unsigned CallFrame::callSiteAsRawBits() const
 {
-    return this[CallFrameSlot::argumentCount].tag();
+    return this[CallFrameSlot::argumentCountIncludingThis].tag();
 }
 
 SUPPRESS_ASAN unsigned CallFrame::unsafeCallSiteAsRawBits() const
 {
-    return this[CallFrameSlot::argumentCount].unsafeTag();
+    return this[CallFrameSlot::argumentCountIncludingThis].unsafeTag();
 }
 
 CallSiteIndex CallFrame::callSiteIndex() const
@@ -117,7 +117,7 @@ const Instruction* CallFrame::currentVPC() const
 void CallFrame::setCurrentVPC(const Instruction* vpc)
 {
     CallSiteIndex callSite(BytecodeIndex(bitwise_cast<uint32_t>(vpc)));
-    this[CallFrameSlot::argumentCount].tag() = callSite.bits();
+    this[CallFrameSlot::argumentCountIncludingThis].tag() = callSite.bits();
 }
 
 unsigned CallFrame::callSiteBitsAsBytecodeOffset() const
@@ -137,7 +137,7 @@ const Instruction* CallFrame::currentVPC() const
 void CallFrame::setCurrentVPC(const Instruction* vpc)
 {
     CallSiteIndex callSite(codeBlock()->bytecodeIndex(vpc));
-    this[CallFrameSlot::argumentCount].tag() = static_cast<int32_t>(callSite.bits());
+    this[CallFrameSlot::argumentCountIncludingThis].tag() = static_cast<int32_t>(callSite.bits());
 }
 
 unsigned CallFrame::callSiteBitsAsBytecodeOffset() const
index cfb7718..8abddb8 100644 (file)
@@ -86,8 +86,8 @@ namespace JSC  {
     struct CallFrameSlot {
         static constexpr int codeBlock = CallerFrameAndPC::sizeInRegisters;
         static constexpr int callee = codeBlock + 1;
-        static constexpr int argumentCount = callee + 1;
-        static constexpr int thisArgument = argumentCount + 1;
+        static constexpr int argumentCountIncludingThis = callee + 1;
+        static constexpr int thisArgument = argumentCountIncludingThis + 1;
         static constexpr int firstArgument = thisArgument + 1;
     };
 
@@ -95,7 +95,7 @@ namespace JSC  {
     // Passed as the first argument to most functions.
     class CallFrame : private Register {
     public:
-        static constexpr int headerSizeInRegisters = CallFrameSlot::argumentCount + 1;
+        static constexpr int headerSizeInRegisters = CallFrameSlot::argumentCountIncludingThis + 1;
 
         // This function should only be called in very specific circumstances
         // when you've guaranteed the callee can't be a Wasm callee, and can
@@ -190,7 +190,7 @@ namespace JSC  {
 
         // Access to arguments as passed. (After capture, arguments may move to a different location.)
         size_t argumentCount() const { return argumentCountIncludingThis() - 1; }
-        size_t argumentCountIncludingThis() const { return this[CallFrameSlot::argumentCount].payload(); }
+        size_t argumentCountIncludingThis() const { return this[CallFrameSlot::argumentCountIncludingThis].payload(); }
         static int argumentOffset(int argument) { return (CallFrameSlot::firstArgument + argument); }
         static int argumentOffsetIncludingThis(int argument) { return (CallFrameSlot::thisArgument + argument); }
 
@@ -251,7 +251,7 @@ namespace JSC  {
         bool isStackOverflowFrame() const;
         bool isWasmFrame() const;
 
-        void setArgumentCountIncludingThis(int count) { static_cast<Register*>(this)[CallFrameSlot::argumentCount].payload() = count; }
+        void setArgumentCountIncludingThis(int count) { static_cast<Register*>(this)[CallFrameSlot::argumentCountIncludingThis].payload() = count; }
         inline void setCallee(JSObject*);
         inline void setCodeBlock(CodeBlock*);
         void setReturnPC(void* value) { callerFrameAndPC().returnPC = reinterpret_cast<const Instruction*>(value); }
index c8df200..9ee9163 100644 (file)
@@ -211,7 +211,7 @@ void AssemblyHelpers::jitAssertIsNull(GPRReg gpr)
 
 void AssemblyHelpers::jitAssertArgumentCountSane()
 {
-    Jump ok = branch32(Below, payloadFor(CallFrameSlot::argumentCount), TrustedImm32(10000000));
+    Jump ok = branch32(Below, payloadFor(CallFrameSlot::argumentCountIncludingThis), TrustedImm32(10000000));
     abortWithReason(AHInsaneArgumentCount);
     ok.link(this);
 }
index 22009c5..34d90c8 100644 (file)
@@ -1527,7 +1527,7 @@ public:
     {
         ASSERT(!inlineCallFrame || inlineCallFrame->isVarargs());
         if (!inlineCallFrame)
-            return VirtualRegister(CallFrameSlot::argumentCount);
+            return VirtualRegister(CallFrameSlot::argumentCountIncludingThis);
         return inlineCallFrame->argumentCountRegister;
     }
 
index 015f03b..11fe37a 100644 (file)
@@ -724,7 +724,7 @@ public:
             GPRReg oldFrameSizeGPR = temp2;
             {
                 GPRReg argCountGPR = oldFrameSizeGPR;
-                load32(Address(framePointerRegister, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset), argCountGPR);
+                load32(Address(framePointerRegister, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset), argCountGPR);
 
                 {
                     GPRReg numParametersGPR = temp1;
@@ -753,7 +753,7 @@ public:
             // The new frame size is just the number of arguments plus the
             // frame header size, aligned
             ASSERT(newFrameSizeGPR != newFramePointer);
-            load32(Address(stackPointerRegister, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC)),
+            load32(Address(stackPointerRegister, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC)),
                 newFrameSizeGPR);
             add32(TrustedImm32(stackAlignmentRegisters() + CallFrame::headerSizeInRegisters - 1), newFrameSizeGPR);
             and32(TrustedImm32(-stackAlignmentRegisters()), newFrameSizeGPR);
index 9690e09..bf43d7b 100644 (file)
@@ -152,7 +152,7 @@ void CallFrameShuffler::dump(PrintStream& out) const
                     out.printf(" %c%8s <- %18s %c ", d, str.data(),
                         recoveryStr.data(), d);
                 }
-            } else if (newReg == VirtualRegister { CallFrameSlot::argumentCount })
+            } else if (newReg == VirtualRegister { CallFrameSlot::argumentCountIncludingThis })
                 out.printf(" %c%8s <- %18zu %c ", d, str.data(), argCount(), d);
             else
                 out.printf(" %c%30s %c ", d, "", d);
@@ -418,7 +418,7 @@ void CallFrameShuffler::prepareForTailCall()
     // old frame (taking into account an argument count higher than
     // the number of parameters), then substracting to it the aligned
     // new frame size (adjusted).
-    m_jit.load32(MacroAssembler::Address(GPRInfo::callFrameRegister, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset), m_newFrameBase);
+    m_jit.load32(MacroAssembler::Address(GPRInfo::callFrameRegister, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset), m_newFrameBase);
     MacroAssembler::Jump argumentCountOK =
         m_jit.branch32(MacroAssembler::BelowOrEqual, m_newFrameBase,
             MacroAssembler::TrustedImm32(m_jit.codeBlock()->numParameters()));
@@ -738,12 +738,12 @@ void CallFrameShuffler::prepareAny()
     // We need to handle 4) first because it implies releasing
     // m_newFrameBase, which could be a wanted register.
     if (verbose)
-        dataLog("   * Storing the argument count into ", VirtualRegister { CallFrameSlot::argumentCount }, "\n");
+        dataLog("   * Storing the argument count into ", VirtualRegister { CallFrameSlot::argumentCountIncludingThis }, "\n");
     m_jit.store32(MacroAssembler::TrustedImm32(0),
-        addressForNew(VirtualRegister { CallFrameSlot::argumentCount }).withOffset(TagOffset));
+        addressForNew(VirtualRegister { CallFrameSlot::argumentCountIncludingThis }).withOffset(TagOffset));
     RELEASE_ASSERT(m_numPassedArgs != UINT_MAX);
     m_jit.store32(MacroAssembler::TrustedImm32(m_numPassedArgs),
-        addressForNew(VirtualRegister { CallFrameSlot::argumentCount }).withOffset(PayloadOffset));
+        addressForNew(VirtualRegister { CallFrameSlot::argumentCountIncludingThis }).withOffset(PayloadOffset));
 
     if (!isSlowPath()) {
         ASSERT(m_newFrameBase != MacroAssembler::stackPointerRegister);
index 17c0eda..efa78bc 100644 (file)
@@ -307,6 +307,7 @@ void JIT::privateCompileMainPass()
         DEFINE_SLOW_OP(create_direct_arguments)
         DEFINE_SLOW_OP(create_scoped_arguments)
         DEFINE_SLOW_OP(create_cloned_arguments)
+        DEFINE_SLOW_OP(create_arguments_butterfly)
         DEFINE_SLOW_OP(create_rest)
         DEFINE_SLOW_OP(create_promise)
         DEFINE_SLOW_OP(new_promise)
@@ -755,7 +756,7 @@ void JIT::compileWithoutLinking(JITCompilationEffort effort)
         emitFunctionPrologue();
         emitPutToCallFrameHeader(m_codeBlock, CallFrameSlot::codeBlock);
 
-        load32(payloadFor(CallFrameSlot::argumentCount), regT1);
+        load32(payloadFor(CallFrameSlot::argumentCountIncludingThis), regT1);
         branch32(AboveOrEqual, regT1, TrustedImm32(m_codeBlock->m_numParameters)).linkTo(beginLabel, this);
 
         m_bytecodeIndex = BytecodeIndex(0);
index bf2306b..c3f8aa0 100644 (file)
@@ -74,7 +74,7 @@ JIT::compileSetupFrame(const Op& bytecode, CallLinkInfo*)
     }
 
     addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister);
-    store32(TrustedImm32(argCount), Address(stackPointerRegister, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC)));
+    store32(TrustedImm32(argCount), Address(stackPointerRegister, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC)));
 }
 
 
@@ -110,7 +110,7 @@ JIT::compileSetupFrame(const Op& bytecode, CallLinkInfo* info)
     move(returnValueGPR, regT1);
 
     // Profile the argument count.
-    load32(Address(regT1, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset), regT2);
+    load32(Address(regT1, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset), regT2);
     load32(info->addressOfMaxArgumentCountIncludingThis(), regT0);
     Jump notBiggest = branch32(Above, regT0, regT2);
     store32(regT2, info->addressOfMaxArgumentCountIncludingThis());
@@ -228,7 +228,7 @@ void JIT::compileOpCall(const Instruction* instruction, unsigned callLinkInfoInd
     // SP holds newCallFrame + sizeof(CallerFrameAndPC), with ArgumentCount initialized.
     auto bytecodeIndex = m_codeBlock->bytecodeIndex(instruction);
     uint32_t locationBits = CallSiteIndex(bytecodeIndex).bits();
-    store32(TrustedImm32(locationBits), Address(callFrameRegister, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + TagOffset));
+    store32(TrustedImm32(locationBits), Address(callFrameRegister, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + TagOffset));
 
     emitGetVirtualRegister(callee, regT0); // regT0 holds callee.
     store64(regT0, Address(stackPointerRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register)) - sizeof(CallerFrameAndPC)));
index 7f6c910..d6dbe09 100644 (file)
@@ -165,7 +165,7 @@ JIT::compileSetupFrame(const Op& bytecode, CallLinkInfo*)
     }
 
     addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister);
-    store32(TrustedImm32(argCount), Address(stackPointerRegister, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC)));
+    store32(TrustedImm32(argCount), Address(stackPointerRegister, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC)));
 }
 
 template<typename Op>
@@ -201,7 +201,7 @@ JIT::compileSetupFrame(const Op& bytecode, CallLinkInfo* info)
     move(returnValueGPR, regT1);
 
     // Profile the argument count.
-    load32(Address(regT1, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset), regT2);
+    load32(Address(regT1, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset), regT2);
     load32(info->addressOfMaxArgumentCountIncludingThis(), regT0);
     Jump notBiggest = branch32(Above, regT0, regT2);
     store32(regT2, info->addressOfMaxArgumentCountIncludingThis());
@@ -289,7 +289,7 @@ void JIT::compileOpCall(const Instruction* instruction, unsigned callLinkInfoInd
     // SP holds newCallFrame + sizeof(CallerFrameAndPC), with ArgumentCount initialized.
     
     uint32_t locationBits = CallSiteIndex(BytecodeIndex(bitwise_cast<uint32_t>(instruction))).bits();
-    store32(TrustedImm32(locationBits), tagFor(CallFrameSlot::argumentCount));
+    store32(TrustedImm32(locationBits), tagFor(CallFrameSlot::argumentCountIncludingThis));
     emitLoad(callee, regT1, regT0); // regT1, regT0 holds callee.
 
     store32(regT0, Address(stackPointerRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC)));
index 22149c9..fce8bb7 100644 (file)
@@ -109,7 +109,7 @@ ALWAYS_INLINE void JIT::updateTopCallFrame()
 #else
     uint32_t locationBits = CallSiteIndex(m_bytecodeIndex).bits();
 #endif
-    store32(TrustedImm32(locationBits), tagFor(CallFrameSlot::argumentCount));
+    store32(TrustedImm32(locationBits), tagFor(CallFrameSlot::argumentCountIncludingThis));
     
     // FIXME: It's not clear that this is needed. JITOperations tend to update the top call frame on
     // the C++ side.
index 7cfa095..8e391c5 100644 (file)
@@ -1546,7 +1546,7 @@ void JIT::emit_op_argument_count(const Instruction* currentInstruction)
 {
     auto bytecode = currentInstruction->as<OpArgumentCount>();
     int dst = bytecode.m_dst.offset();
-    load32(payloadFor(CallFrameSlot::argumentCount), regT0);
+    load32(payloadFor(CallFrameSlot::argumentCountIncludingThis), regT0);
     sub32(TrustedImm32(1), regT0);
     JSValueRegs result = JSValueRegs::withTwoAvailableRegs(regT0, regT1);
     boxInt32(regT0, result);
@@ -1558,7 +1558,7 @@ void JIT::emit_op_get_rest_length(const Instruction* currentInstruction)
     auto bytecode = currentInstruction->as<OpGetRestLength>();
     int dst = bytecode.m_dst.offset();
     unsigned numParamsToSkip = bytecode.m_numParametersToSkip;
-    load32(payloadFor(CallFrameSlot::argumentCount), regT0);
+    load32(payloadFor(CallFrameSlot::argumentCountIncludingThis), regT0);
     sub32(TrustedImm32(1), regT0);
     Jump zeroLength = branch32(LessThanOrEqual, regT0, Imm32(numParamsToSkip));
     sub32(Imm32(numParamsToSkip), regT0);
@@ -1594,7 +1594,7 @@ void JIT::emit_op_get_argument(const Instruction* currentInstruction)
     JSValueRegs resultRegs(regT1, regT0);
 #endif
 
-    load32(payloadFor(CallFrameSlot::argumentCount), regT2);
+    load32(payloadFor(CallFrameSlot::argumentCountIncludingThis), regT2);
     Jump argumentOutOfBounds = branch32(LessThanOrEqual, regT2, TrustedImm32(index));
     loadValue(addressFor(CallFrameSlot::thisArgument + index), resultRegs);
     Jump done = jump();
index 3887305..0952591 100644 (file)
@@ -88,7 +88,7 @@ static void emitSetupVarargsFrameFastCase(VM& vm, CCallHelpers& jit, GPRReg numU
     jit.addPtr(CCallHelpers::TrustedImm32(sizeof(CallerFrameAndPC)), scratchGPR2, CCallHelpers::stackPointerRegister);
 
     // Initialize ArgumentCount.
-    jit.store32(scratchGPR1, CCallHelpers::Address(scratchGPR2, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset));
+    jit.store32(scratchGPR1, CCallHelpers::Address(scratchGPR2, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset));
 
     // Copy arguments.
     jit.signExtend32ToPtr(scratchGPR1, scratchGPR1);
@@ -128,7 +128,7 @@ void emitSetupVarargsFrameFastCase(VM& vm, CCallHelpers& jit, GPRReg numUsedSlot
             firstArgumentReg = VirtualRegister(0);
     } else {
         argumentCountRecovery = ValueRecovery::displacedInJSStack(
-            VirtualRegister(CallFrameSlot::argumentCount), DataFormatInt32);
+            VirtualRegister(CallFrameSlot::argumentCountIncludingThis), DataFormatInt32);
         firstArgumentReg = VirtualRegister(CallFrame::argumentOffset(0));
     }
     emitSetupVarargsFrameFastCase(vm, jit, numUsedSlotsGPR, scratchGPR1, scratchGPR2, scratchGPR3, argumentCountRecovery, firstArgumentReg, firstVarArgOffset, slowCase);
index 4f567d0..a11622e 100644 (file)
@@ -43,7 +43,7 @@ namespace JSC {
             emitFunctionPrologue();
             emitSaveThenMaterializeTagRegisters();
             // Check that we have the expected number of arguments
-            m_failures.append(branch32(NotEqual, payloadFor(CallFrameSlot::argumentCount), TrustedImm32(expectedArgCount + 1)));
+            m_failures.append(branch32(NotEqual, payloadFor(CallFrameSlot::argumentCountIncludingThis), TrustedImm32(expectedArgCount + 1)));
         }
         
         explicit SpecializedThunkJIT(VM& vm)
index 898ff17..4a2bdda 100644 (file)
@@ -401,7 +401,7 @@ MacroAssemblerCodeRef<JITThunkPtrTag> arityFixupGenerator(VM& vm)
     jit.storePtr(GPRInfo::regT3, JSInterfaceJIT::Address(GPRInfo::callFrameRegister, CallFrame::returnPCOffset()));
 #endif
     jit.move(JSInterfaceJIT::callFrameRegister, JSInterfaceJIT::regT3);
-    jit.load32(JSInterfaceJIT::addressFor(CallFrameSlot::argumentCount), JSInterfaceJIT::argumentGPR2);
+    jit.load32(JSInterfaceJIT::addressFor(CallFrameSlot::argumentCountIncludingThis), JSInterfaceJIT::argumentGPR2);
     jit.add32(JSInterfaceJIT::TrustedImm32(CallFrame::headerSizeInRegisters), JSInterfaceJIT::argumentGPR2);
 
     // Check to see if we have extra slots we can use
@@ -462,7 +462,7 @@ MacroAssemblerCodeRef<JITThunkPtrTag> arityFixupGenerator(VM& vm)
     jit.ret();
 #else // USE(JSVALUE64) section above, USE(JSVALUE32_64) section below.
     jit.move(JSInterfaceJIT::callFrameRegister, JSInterfaceJIT::regT3);
-    jit.load32(JSInterfaceJIT::addressFor(CallFrameSlot::argumentCount), JSInterfaceJIT::argumentGPR2);
+    jit.load32(JSInterfaceJIT::addressFor(CallFrameSlot::argumentCountIncludingThis), JSInterfaceJIT::argumentGPR2);
     jit.add32(JSInterfaceJIT::TrustedImm32(CallFrame::headerSizeInRegisters), JSInterfaceJIT::argumentGPR2);
 
     // Check to see if we have extra slots we can use
@@ -1129,7 +1129,7 @@ MacroAssemblerCodeRef<JITThunkPtrTag> randomThunkGenerator(VM& vm)
 #endif
 }
 
-MacroAssemblerCodeRef<JITThunkPtrTag> boundThisNoArgsFunctionCallGenerator(VM& vm)
+MacroAssemblerCodeRef<JITThunkPtrTag> boundFunctionCallGenerator(VM& vm)
 {
     CCallHelpers jit;
     
@@ -1137,7 +1137,7 @@ MacroAssemblerCodeRef<JITThunkPtrTag> boundThisNoArgsFunctionCallGenerator(VM& v
     
     // Set up our call frame.
     jit.storePtr(CCallHelpers::TrustedImmPtr(nullptr), CCallHelpers::addressFor(CallFrameSlot::codeBlock));
-    jit.store32(CCallHelpers::TrustedImm32(0), CCallHelpers::tagFor(CallFrameSlot::argumentCount));
+    jit.store32(CCallHelpers::TrustedImm32(0), CCallHelpers::tagFor(CallFrameSlot::argumentCountIncludingThis));
 
     unsigned extraStackNeeded = 0;
     if (unsigned stackMisalignment = sizeof(CallerFrameAndPC) % stackAlignmentBytes())
@@ -1158,7 +1158,15 @@ MacroAssemblerCodeRef<JITThunkPtrTag> boundThisNoArgsFunctionCallGenerator(VM& v
     //
     // That's really all there is to this. We have all the registers we need to do it.
     
-    jit.load32(CCallHelpers::payloadFor(CallFrameSlot::argumentCount), GPRInfo::regT1);
+    jit.loadCell(CCallHelpers::addressFor(CallFrameSlot::callee), GPRInfo::regT0);
+    jit.loadPtr(CCallHelpers::Address(GPRInfo::regT0, JSBoundFunction::offsetOfBoundArgs()), GPRInfo::regT2);
+    jit.load32(CCallHelpers::payloadFor(CallFrameSlot::argumentCountIncludingThis), GPRInfo::regT1);
+    jit.move(GPRInfo::regT1, GPRInfo::regT3);
+    auto noArgs = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT2);
+    jit.load32(CCallHelpers::Address(GPRInfo::regT2, JSImmutableButterfly::offsetOfPublicLength()), GPRInfo::regT2);
+    jit.add32(GPRInfo::regT2, GPRInfo::regT1);
+    jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT1);
+    noArgs.link(&jit);
     jit.add32(CCallHelpers::TrustedImm32(CallFrame::headerSizeInRegisters - CallerFrameAndPC::sizeInRegisters), GPRInfo::regT1, GPRInfo::regT2);
     jit.lshift32(CCallHelpers::TrustedImm32(3), GPRInfo::regT2);
     jit.add32(CCallHelpers::TrustedImm32(stackAlignmentBytes() - 1), GPRInfo::regT2);
@@ -1167,12 +1175,11 @@ MacroAssemblerCodeRef<JITThunkPtrTag> boundThisNoArgsFunctionCallGenerator(VM& v
     if (extraStackNeeded)
         jit.add32(CCallHelpers::TrustedImm32(extraStackNeeded), GPRInfo::regT2);
     
-    // At this point regT1 has the actual argument count and regT2 has the amount of stack we will need.
+    // At this point regT1 has the actual argument count, regT2 has the amount of stack we will need, and regT3 has the passed argument count.
     // Check to see if we have enough stack space.
     
     jit.negPtr(GPRInfo::regT2);
     jit.addPtr(CCallHelpers::stackPointerRegister, GPRInfo::regT2);
-    jit.loadCell(CCallHelpers::addressFor(CallFrameSlot::callee), GPRInfo::regT3);
     CCallHelpers::Jump haveStackSpace = jit.branchPtr(CCallHelpers::BelowOrEqual, CCallHelpers::AbsoluteAddress(vm.addressOfSoftStackLimit()), GPRInfo::regT2);
 
     // Throw Stack Overflow exception
@@ -1190,31 +1197,44 @@ MacroAssemblerCodeRef<JITThunkPtrTag> boundThisNoArgsFunctionCallGenerator(VM& v
 
     // Do basic callee frame setup, including 'this'.
     
-    jit.store32(GPRInfo::regT1, CCallHelpers::calleeFramePayloadSlot(CallFrameSlot::argumentCount));
+    jit.store32(GPRInfo::regT1, CCallHelpers::calleeFramePayloadSlot(CallFrameSlot::argumentCountIncludingThis));
     
-    JSValueRegs valueRegs = JSValueRegs::withTwoAvailableRegs(GPRInfo::regT0, GPRInfo::regT2);
-    jit.loadValue(CCallHelpers::Address(GPRInfo::regT3, JSBoundFunction::offsetOfBoundThis()), valueRegs);
+    JSValueRegs valueRegs = JSValueRegs::withTwoAvailableRegs(GPRInfo::regT4, GPRInfo::regT2);
+    jit.loadValue(CCallHelpers::Address(GPRInfo::regT0, JSBoundFunction::offsetOfBoundThis()), valueRegs);
     jit.storeValue(valueRegs, CCallHelpers::calleeArgumentSlot(0));
 
-    jit.loadPtr(CCallHelpers::Address(GPRInfo::regT3, JSBoundFunction::offsetOfTargetFunction()), GPRInfo::regT3);
-    jit.storeCell(GPRInfo::regT3, CCallHelpers::calleeFrameSlot(CallFrameSlot::callee));
-    
     // OK, now we can start copying. This is a simple matter of copying parameters from the caller's
-    // frame to the callee's frame. Note that we know that regT1 (the argument count) must be at
+    // frame to the callee's frame. Note that we know that regT3 (the argument count) must be at
     // least 1.
+    jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT3);
     jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT1);
-    CCallHelpers::Jump done = jit.branchTest32(CCallHelpers::Zero, GPRInfo::regT1);
+    CCallHelpers::Jump done = jit.branchTest32(CCallHelpers::Zero, GPRInfo::regT3);
     
     CCallHelpers::Label loop = jit.label();
+    jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT3);
     jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT1);
-    jit.loadValue(CCallHelpers::addressFor(virtualRegisterForArgument(1)).indexedBy(GPRInfo::regT1, CCallHelpers::TimesEight), valueRegs);
+    jit.loadValue(CCallHelpers::addressFor(virtualRegisterForArgument(1)).indexedBy(GPRInfo::regT3, CCallHelpers::TimesEight), valueRegs);
     jit.storeValue(valueRegs, CCallHelpers::calleeArgumentSlot(1).indexedBy(GPRInfo::regT1, CCallHelpers::TimesEight));
-    jit.branchTest32(CCallHelpers::NonZero, GPRInfo::regT1).linkTo(loop, &jit);
+    jit.branchTest32(CCallHelpers::NonZero, GPRInfo::regT3).linkTo(loop, &jit);
     
     done.link(&jit);
+    auto noArgs2 = jit.branchTest32(CCallHelpers::Zero, GPRInfo::regT1);
+
+    jit.loadPtr(CCallHelpers::Address(GPRInfo::regT0, JSBoundFunction::offsetOfBoundArgs()), GPRInfo::regT3);
+
+    CCallHelpers::Label loopBound = jit.label();
+    jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT1);
+    jit.loadValue(CCallHelpers::BaseIndex(GPRInfo::regT3, GPRInfo::regT1, CCallHelpers::TimesEight, JSImmutableButterfly::offsetOfData() + sizeof(WriteBarrier<Unknown>)), valueRegs);
+    jit.storeValue(valueRegs, CCallHelpers::calleeArgumentSlot(1).indexedBy(GPRInfo::regT1, CCallHelpers::TimesEight));
+    jit.branchTest32(CCallHelpers::NonZero, GPRInfo::regT1).linkTo(loopBound, &jit);
+
+    noArgs2.link(&jit);
+
+    jit.loadPtr(CCallHelpers::Address(GPRInfo::regT0, JSBoundFunction::offsetOfTargetFunction()), GPRInfo::regT2);
+    jit.storeCell(GPRInfo::regT2, CCallHelpers::calleeFrameSlot(CallFrameSlot::callee));
     
     jit.loadPtr(
-        CCallHelpers::Address(GPRInfo::regT3, JSFunction::offsetOfExecutable()),
+        CCallHelpers::Address(GPRInfo::regT2, JSFunction::offsetOfExecutable()),
         GPRInfo::regT0);
     jit.loadPtr(
         CCallHelpers::Address(
index f861ee8..7b1083b 100644 (file)
@@ -68,6 +68,6 @@ MacroAssemblerCodeRef<JITThunkPtrTag> imulThunkGenerator(VM&);
 MacroAssemblerCodeRef<JITThunkPtrTag> randomThunkGenerator(VM&);
 MacroAssemblerCodeRef<JITThunkPtrTag> truncThunkGenerator(VM&);
 
-MacroAssemblerCodeRef<JITThunkPtrTag> boundThisNoArgsFunctionCallGenerator(VM&);
+MacroAssemblerCodeRef<JITThunkPtrTag> boundFunctionCallGenerator(VM&);
 }
 #endif // ENABLE(JIT)
index 1ce27c5..207f66a 100644 (file)
@@ -51,6 +51,7 @@
 #include "JSModuleLoader.h"
 #include "JSNativeStdFunction.h"
 #include "JSONObject.h"
+#include "JSObjectInlines.h"
 #include "JSSourceCode.h"
 #include "JSString.h"
 #include "JSTypedArrays.h"
index 2fde107..f50a17b 100644 (file)
@@ -112,8 +112,8 @@ void Data::performAssertions(VM& vm)
     ASSERT(CallFrame::returnPCOffset() == CallFrame::callerFrameOffset() + MachineRegisterSize);
     ASSERT(CallFrameSlot::codeBlock * sizeof(Register) == CallFrame::returnPCOffset() + MachineRegisterSize);
     STATIC_ASSERT(CallFrameSlot::callee * sizeof(Register) == CallFrameSlot::codeBlock * sizeof(Register) + SlotSize);
-    STATIC_ASSERT(CallFrameSlot::argumentCount * sizeof(Register) == CallFrameSlot::callee * sizeof(Register) + SlotSize);
-    STATIC_ASSERT(CallFrameSlot::thisArgument * sizeof(Register) == CallFrameSlot::argumentCount * sizeof(Register) + SlotSize);
+    STATIC_ASSERT(CallFrameSlot::argumentCountIncludingThis * sizeof(Register) == CallFrameSlot::callee * sizeof(Register) + SlotSize);
+    STATIC_ASSERT(CallFrameSlot::thisArgument * sizeof(Register) == CallFrameSlot::argumentCountIncludingThis * sizeof(Register) + SlotSize);
     STATIC_ASSERT(CallFrame::headerSizeInRegisters == CallFrameSlot::thisArgument);
 
     ASSERT(CallFrame::argumentOffsetIncludingThis(0) == CallFrameSlot::thisArgument);
index e1734dc..c8a07f0 100644 (file)
@@ -177,8 +177,8 @@ const CallerFrame = 0
 const ReturnPC = CallerFrame + MachineRegisterSize
 const CodeBlock = ReturnPC + MachineRegisterSize
 const Callee = CodeBlock + SlotSize
-const ArgumentCount = Callee + SlotSize
-const ThisArgumentOffset = ArgumentCount + SlotSize
+const ArgumentCountIncludingThis = Callee + SlotSize
+const ThisArgumentOffset = ArgumentCountIncludingThis + SlotSize
 const FirstArgumentOffset = ThisArgumentOffset + SlotSize
 const CallFrameHeaderSize = ThisArgumentOffset
 
@@ -530,7 +530,7 @@ const FunctionCode = constexpr FunctionCode
 const ModuleCode = constexpr ModuleCode
 
 # The interpreter steals the tag word of the argument count.
-const LLIntReturnPC = ArgumentCount + TagOffset
+const LLIntReturnPC = ArgumentCountIncludingThis + TagOffset
 
 # String flags.
 const isRopeInPointer = constexpr JSString::isRopeInPointer
@@ -1015,7 +1015,7 @@ end
 macro prepareForTailCall(callee, temp1, temp2, temp3, callPtrTag)
     restoreCalleeSavesUsedByLLInt()
 
-    loadi PayloadOffset + ArgumentCount[cfr], temp2
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], temp2
     loadp CodeBlock[cfr], temp1
     loadi CodeBlock::m_numParameters[temp1], temp1
     bilteq temp1, temp2, .noArityFixup
@@ -1030,7 +1030,7 @@ macro prepareForTailCall(callee, temp1, temp2, temp3, callPtrTag)
     move cfr, temp1
     addp temp2, temp1
 
-    loadi PayloadOffset + ArgumentCount[sp], temp2
+    loadi PayloadOffset + ArgumentCountIncludingThis[sp], temp2
     # We assume < 2^28 arguments
     muli SlotSize, temp2
     addi StackAlignment - 1 + CallFrameHeaderSize, temp2
@@ -1091,7 +1091,7 @@ macro getterSetterOSRExitReturnPoint(opName, size)
     defineOSRExitReturnLabel(opName, size)
 
     restoreStackPointerAfterCall()
-    loadi ArgumentCount + TagOffset[cfr], PC
+    loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
 end
 
 macro arrayProfile(offset, cellAndIndexingType, metadata, scratch)
@@ -1588,6 +1588,7 @@ macro slowPathOp(opcodeName)
 end
 
 slowPathOp(create_cloned_arguments)
+slowPathOp(create_arguments_butterfly)
 slowPathOp(create_direct_arguments)
 slowPathOp(create_lexical_environment)
 slowPathOp(create_rest)
index 1972291..ae66994 100644 (file)
@@ -93,7 +93,7 @@ end
 
 # After calling, calling bytecode is claiming input registers are not used.
 macro dispatchAfterCall(size, opcodeStruct, dispatch)
-    loadi ArgumentCount + TagOffset[cfr], PC
+    loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
     get(size, opcodeStruct, m_dst, t3)
     storei r1, TagOffset[cfr, t3, 8]
     storei r0, PayloadOffset[cfr, t3, 8]
@@ -433,7 +433,7 @@ end
 
 # Call a slowPath for call opcodes.
 macro callCallSlowPath(slowPath, action)
-    storep PC, ArgumentCount + TagOffset[cfr]
+    storep PC, ArgumentCountIncludingThis + TagOffset[cfr]
     move cfr, a0
     move PC, a1
     cCall2(slowPath)
@@ -441,19 +441,19 @@ macro callCallSlowPath(slowPath, action)
 end
 
 macro callTrapHandler(throwHandler)
-    storei PC, ArgumentCount + TagOffset[cfr]
+    storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
     move cfr, a0
     move PC, a1
     cCall2(_llint_slow_path_handle_traps)
     btpnz r0, throwHandler
-    loadi ArgumentCount + TagOffset[cfr], PC
+    loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
 end
 
 macro checkSwitchToJITForLoop()
     checkSwitchToJIT(
         1,
         macro ()
-            storei PC, ArgumentCount + TagOffset[cfr]
+            storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
             move cfr, a0
             move PC, a1
             cCall2(_llint_loop_osr)
@@ -461,7 +461,7 @@ macro checkSwitchToJITForLoop()
             move r1, sp
             jmp r0
         .recover:
-            loadi ArgumentCount + TagOffset[cfr], PC
+            loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
         end)
 end
 
@@ -637,7 +637,7 @@ end
 
 # Expects that CodeBlock is in t1, which is what prologue() leaves behind.
 macro functionArityCheck(doneLabel, slowPath)
-    loadi PayloadOffset + ArgumentCount[cfr], t0
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], t0
     biaeq t0, CodeBlock::m_numParameters[t1], doneLabel
     move cfr, a0
     move PC, a1
@@ -655,7 +655,7 @@ macro functionArityCheck(doneLabel, slowPath)
 .noError:
     move r1, t1 # r1 contains slotsToAdd.
     btiz t1, .continue
-    loadi PayloadOffset + ArgumentCount[cfr], t2
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], t2
     addi CallFrameHeaderSlots, t2
 
     // Check if there are some unaligned slots we can use
@@ -733,7 +733,7 @@ _llint_op_enter:
 
 llintOpWithProfile(op_get_argument, OpGetArgument, macro (size, get, dispatch, return)
     get(m_index, t2)
-    loadi PayloadOffset + ArgumentCount[cfr], t0
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], t0
     bilteq t0, t2, .opGetArgumentOutOfBounds
     loadi ThisArgumentOffset + TagOffset[cfr, t2, 8], t0
     loadi ThisArgumentOffset + PayloadOffset[cfr, t2, 8], t3
@@ -745,7 +745,7 @@ end)
 
 
 llintOpWithReturn(op_argument_count, OpArgumentCount, macro (size, get, dispatch, return)
-    loadi PayloadOffset + ArgumentCount[cfr], t0
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], t0
     subi 1, t0
     return(Int32Tag, t0)
 end)
@@ -1925,8 +1925,8 @@ macro commonCallOp(opcodeName, slowPath, opcodeStruct, prepareCall, prologue)
         addp cfr, t3  # t3 contains the new value of cfr
         storei t2, Callee + PayloadOffset[t3]
         getu(size, opcodeStruct, m_argc, t2)
-        storei PC, ArgumentCount + TagOffset[cfr]
-        storei t2, ArgumentCount + PayloadOffset[t3]
+        storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
+        storei t2, ArgumentCountIncludingThis + PayloadOffset[t3]
         storei CellTag, Callee + TagOffset[t3]
         move t3, sp
         prepareCall(%opcodeStruct%::Metadata::m_callLinkInfo.m_machineCodeTarget[t5], t2, t3, t4, JSEntryPtrTag)
@@ -2534,7 +2534,7 @@ end)
 
 
 llintOpWithReturn(op_get_rest_length, OpGetRestLength, macro (size, get, dispatch, return)
-    loadi PayloadOffset + ArgumentCount[cfr], t0
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], t0
     subi 1, t0
     getu(size, OpGetRestLength, m_numParametersToSkip, t1)
     bilteq t0, t1, .storeZero
index 4039c62..ea43ab6 100644 (file)
@@ -92,7 +92,7 @@ end
 
 # After calling, calling bytecode is claiming input registers are not used.
 macro dispatchAfterCall(size, opcodeStruct, dispatch)
-    loadi ArgumentCount + TagOffset[cfr], PC
+    loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
     loadp CodeBlock[cfr], PB
     loadp CodeBlock::m_instructionsRawPointer[PB], PB
     get(size, opcodeStruct, m_dst, t1)
@@ -217,7 +217,7 @@ macro doVMEntry(makeCall)
     move (constexpr ProtoCallFrame::numberOfRegisters), t3
 
 .copyHeaderLoop:
-    # Copy the CodeBlock/Callee/ArgumentCount/|this| from protoCallFrame into the callee frame.
+    # Copy the CodeBlock/Callee/ArgumentCountIncludingThis/|this| from protoCallFrame into the callee frame.
     subi 1, t3
     loadq [protoCallFrame, t3, 8], extraTempReg
     storeq extraTempReg, CodeBlock[sp, t3, 8]
@@ -390,7 +390,7 @@ end
 
 # Call a slow path for call opcodes.
 macro callCallSlowPath(slowPath, action)
-    storei PC, ArgumentCount + TagOffset[cfr]
+    storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
     prepareStateForCCall()
     move cfr, a0
     move PC, a1
@@ -399,20 +399,20 @@ macro callCallSlowPath(slowPath, action)
 end
 
 macro callTrapHandler(throwHandler)
-    storei PC, ArgumentCount + TagOffset[cfr]
+    storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
     prepareStateForCCall()
     move cfr, a0
     move PC, a1
     cCall2(_llint_slow_path_handle_traps)
     btpnz r0, throwHandler
-    loadi ArgumentCount + TagOffset[cfr], PC
+    loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
 end
 
 macro checkSwitchToJITForLoop()
     checkSwitchToJIT(
         1,
         macro()
-            storei PC, ArgumentCount + TagOffset[cfr]
+            storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
             prepareStateForCCall()
             move cfr, a0
             move PC, a1
@@ -421,7 +421,7 @@ macro checkSwitchToJITForLoop()
             move r1, sp
             jmp r0, JSEntryPtrTag
         .recover:
-            loadi ArgumentCount + TagOffset[cfr], PC
+            loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
         end)
 end
 
@@ -619,7 +619,7 @@ end
 
 # Expects that CodeBlock is in t1, which is what prologue() leaves behind.
 macro functionArityCheck(doneLabel, slowPath)
-    loadi PayloadOffset + ArgumentCount[cfr], t0
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], t0
     biaeq t0, CodeBlock::m_numParameters[t1], doneLabel
     prepareStateForCCall()
     move cfr, a0
@@ -638,7 +638,7 @@ macro functionArityCheck(doneLabel, slowPath)
 .noError:
     move r1, t1 # r1 contains slotsToAdd.
     btiz t1, .continue
-    loadi PayloadOffset + ArgumentCount[cfr], t2
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], t2
     addi CallFrameHeaderSlots, t2
 
     // Check if there are some unaligned slots we can use
@@ -723,7 +723,7 @@ _llint_op_enter:
 
 llintOpWithProfile(op_get_argument, OpGetArgument, macro (size, get, dispatch, return)
     get(m_index, t2)
-    loadi PayloadOffset + ArgumentCount[cfr], t0
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], t0
     bilteq t0, t2, .opGetArgumentOutOfBounds
     loadq ThisArgumentOffset[cfr, t2, 8], t0
     return(t0)
@@ -734,7 +734,7 @@ end)
 
 
 llintOpWithReturn(op_argument_count, OpArgumentCount, macro (size, get, dispatch, return)
-    loadi PayloadOffset + ArgumentCount[cfr], t0
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], t0
     subi 1, t0
     orq TagNumber, t0
     return(t0)
@@ -2057,8 +2057,8 @@ macro commonCallOp(opcodeName, slowPath, opcodeStruct, prepareCall, prologue)
         addp cfr, t3
         storeq t2, Callee[t3]
         getu(size, opcodeStruct, m_argc, t2)
-        storei PC, ArgumentCount + TagOffset[cfr]
-        storei t2, ArgumentCount + PayloadOffset[t3]
+        storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
+        storei t2, ArgumentCountIncludingThis + PayloadOffset[t3]
         move t3, sp
         prepareCall(%opcodeStruct%::Metadata::m_callLinkInfo.m_machineCodeTarget[t5], t2, t3, t4, JSEntryPtrTag)
         callTargetFunction(opcodeName, size, opcodeStruct, dispatch, %opcodeStruct%::Metadata::m_callLinkInfo.m_machineCodeTarget[t5], JSEntryPtrTag)
@@ -2624,7 +2624,7 @@ end)
 
 
 llintOpWithReturn(op_get_rest_length, OpGetRestLength, macro (size, get, dispatch, return)
-    loadi PayloadOffset + ArgumentCount[cfr], t0
+    loadi PayloadOffset + ArgumentCountIncludingThis[cfr], t0
     subi 1, t0
     getu(size, OpGetRestLength, m_numParametersToSkip, t1)
     bilteq t0, t1, .storeZero
index 098fab8..65768fa 100644 (file)
@@ -150,7 +150,7 @@ macro checkSwitchToJITForLoop()
     checkSwitchToJIT(
         1,
         macro()
-            storei PC, ArgumentCount + TagOffset[cfr]
+            storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
             prepareStateForCCall()
             move cfr, a0
             move PC, a1
@@ -163,7 +163,7 @@ macro checkSwitchToJITForLoop()
             move r0, a0
             jmp r1, WasmEntryPtrTag
         .recover:
-            loadi ArgumentCount + TagOffset[cfr], PC
+            loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
         end)
 end
 
@@ -229,7 +229,7 @@ macro reloadMemoryRegistersFromInstance(instance, scratch1, scratch2)
 end
 
 macro throwException(exception)
-    storei constexpr Wasm::ExceptionType::%exception%, ArgumentCount + PayloadOffset[cfr]
+    storei constexpr Wasm::ExceptionType::%exception%, ArgumentCountIncludingThis + PayloadOffset[cfr]
     jmp _wasm_throw_from_slow_path_trampoline
 end
 
@@ -243,7 +243,7 @@ macro callWasmSlowPath(slowPath)
 end
 
 macro callWasmCallSlowPath(slowPath, action)
-    storei PC, ArgumentCount + TagOffset[cfr]
+    storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
     prepareStateForCCall()
     move cfr, a0
     move PC, a1
@@ -494,8 +494,8 @@ op(wasm_throw_from_slow_path_trampoline, macro ()
     move cfr, a0
     addp PB, PC, a1
     move wasmInstance, a2
-    # Slow paths and the throwException macro store the exception code in the ArgumentCount slot
-    loadi ArgumentCount + PayloadOffset[cfr], a3
+    # Slow paths and the throwException macro store the exception code in the ArgumentCountIncludingThis slot
+    loadi ArgumentCountIncludingThis + PayloadOffset[cfr], a3
     cCall4(_slow_path_wasm_throw_exception)
 
     jmp r0, ExceptionHandlerPtrTag
@@ -679,7 +679,7 @@ macro slowPathForWasmCall(ctx, slowPath, storeWasmInstance)
         macro (callee, targetWasmInstance)
             move callee, ws0
 
-            loadi ArgumentCount + TagOffset[cfr], PC
+            loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
 
             # the call might throw (e.g. indirect call with bad signature)
             btpz targetWasmInstance, .throw
@@ -718,7 +718,7 @@ macro slowPathForWasmCall(ctx, slowPath, storeWasmInstance)
             # need to preserve its current value since it might contain a return value
             move PC, memoryBase
             move PB, wasmInstance
-            loadi ArgumentCount + TagOffset[cfr], PC
+            loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
             loadp CodeBlock[cfr], PB
             loadp Wasm::FunctionCodeBlock::m_instructionsRawPointer[PB], PB
 
@@ -740,7 +740,7 @@ macro slowPathForWasmCall(ctx, slowPath, storeWasmInstance)
                 stored fpr, CallFrameHeaderSize + offset[ws1, ws0, 8]
             end)
 
-            loadi ArgumentCount + TagOffset[cfr], PC
+            loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
 
             storeWasmInstance(wasmInstance)
             reloadMemoryRegistersFromInstance(wasmInstance, ws0, ws1)
index f358d48..87716d2 100644 (file)
@@ -222,6 +222,19 @@ SLOW_PATH_DECL(slow_path_create_cloned_arguments)
     RETURN(ClonedArguments::createWithMachineFrame(globalObject, callFrame, ArgumentsMode::Cloned));
 }
 
+SLOW_PATH_DECL(slow_path_create_arguments_butterfly)
+{
+    BEGIN();
+    auto bytecode = pc->as<OpCreateArgumentsButterfly>();
+    int32_t argumentCount = callFrame->argumentCount();
+    JSImmutableButterfly* butterfly = JSImmutableButterfly::tryCreate(vm, vm.immutableButterflyStructures[arrayIndexFromIndexingType(CopyOnWriteArrayWithContiguous) - NumberOfIndexingShapes].get(), argumentCount);
+    if (!butterfly)
+        THROW(createOutOfMemoryError(globalObject));
+    for (int32_t index = 0; index < argumentCount; ++index)
+        butterfly->setIndex(vm, index, callFrame->uncheckedArgument(index));
+    RETURN(butterfly);
+}
+
 SLOW_PATH_DECL(slow_path_create_this)
 {
     BEGIN();
index 46bfe15..71e785e 100644 (file)
@@ -320,6 +320,7 @@ SLOW_PATH_HIDDEN_DECL(slow_path_construct_arityCheck);
 SLOW_PATH_HIDDEN_DECL(slow_path_create_direct_arguments);
 SLOW_PATH_HIDDEN_DECL(slow_path_create_scoped_arguments);
 SLOW_PATH_HIDDEN_DECL(slow_path_create_cloned_arguments);
+SLOW_PATH_HIDDEN_DECL(slow_path_create_arguments_butterfly);
 SLOW_PATH_HIDDEN_DECL(slow_path_create_this);
 SLOW_PATH_HIDDEN_DECL(slow_path_enter);
 SLOW_PATH_HIDDEN_DECL(slow_path_get_callee);
index 8062f1e..958105d 100644 (file)
@@ -57,7 +57,7 @@ inline bool isCall(CodeSpecializationKind kind)
 
 class ExecutableBase : public JSCell {
     friend class JIT;
-    friend MacroAssemblerCodeRef<JITThunkPtrTag> boundThisNoArgsFunctionCallGenerator(VM*);
+    friend MacroAssemblerCodeRef<JITThunkPtrTag> boundFunctionCallGenerator(VM*);
 
 protected:
     ExecutableBase(VM& vm, Structure* structure)
index 021b8cc..4f749a8 100644 (file)
@@ -69,7 +69,10 @@ FunctionRareData::FunctionRareData(VM& vm)
     // the first allocation don't disable optimizations. This isn't super important, since the
     // function is unlikely to allocate a rare data until the first allocation anyway.
     , m_allocationProfileWatchpointSet(ClearWatchpoint)
+    , m_hasReifiedLength(false)
     , m_hasReifiedName(false)
+    , m_hasModifiedLength(false)
+    , m_hasModifiedName(false)
 {
 }
 
index 1f32a3c..bda5d7a 100644 (file)
@@ -73,8 +73,6 @@ public:
     static inline ptrdiff_t offsetOfInternalFunctionAllocationProfile() { return OBJECT_OFFSETOF(FunctionRareData, m_internalFunctionAllocationProfile); }
     static inline ptrdiff_t offsetOfBoundFunctionStructure() { return OBJECT_OFFSETOF(FunctionRareData, m_boundFunctionStructure); }
     static inline ptrdiff_t offsetOfAllocationProfileClearingWatchpoint() { return OBJECT_OFFSETOF(FunctionRareData, m_allocationProfileClearingWatchpoint); }
-    static inline ptrdiff_t offsetOfHasReifiedLength() { return OBJECT_OFFSETOF(FunctionRareData, m_hasReifiedLength); }
-    static inline ptrdiff_t offsetOfHasReifiedName() { return OBJECT_OFFSETOF(FunctionRareData, m_hasReifiedName); }
 
     ObjectAllocationProfileWithPrototype* objectAllocationProfile()
     {
@@ -121,6 +119,17 @@ public:
     bool hasReifiedName() const { return m_hasReifiedName; }
     void setHasReifiedName() { m_hasReifiedName = true; }
 
+    bool hasModifiedLength() const { return m_hasModifiedLength; }
+    void setHasModifiedLength()
+    {
+        m_hasModifiedLength = true;
+    }
+    bool hasModifiedName() const { return m_hasModifiedName; }
+    void setHasModifiedName()
+    {
+        m_hasModifiedName = true;
+    }
+
     bool hasAllocationProfileClearingWatchpoint() const { return !!m_allocationProfileClearingWatchpoint; }
     Watchpoint* createAllocationProfileClearingWatchpoint();
     class AllocationProfileClearingWatchpoint;
@@ -150,8 +159,10 @@ private:
     InternalFunctionAllocationProfile m_internalFunctionAllocationProfile;
     WriteBarrier<Structure> m_boundFunctionStructure;
     std::unique_ptr<AllocationProfileClearingWatchpoint> m_allocationProfileClearingWatchpoint;
-    bool m_hasReifiedLength { false };
-    bool m_hasReifiedName { false };
+    bool m_hasReifiedLength : 1;
+    bool m_hasReifiedName : 1;
+    bool m_hasModifiedLength : 1;
+    bool m_hasModifiedName : 1;
 };
 
 class FunctionRareData::AllocationProfileClearingWatchpoint final : public Watchpoint {
index d44232a..90c0212 100644 (file)
@@ -127,7 +127,7 @@ EncodedJSValue JSC_HOST_CALL IntlCollatorPrototypeGetterCompare(JSGlobalObject*
         JSFunction* targetObject = JSFunction::create(vm, globalObject, 2, "compare"_s, IntlCollatorFuncCompare, NoIntrinsic);
 
         // c. Let bc be BoundFunctionCreate(F, «this value»).
-        boundCompare = JSBoundFunction::create(vm, globalObject, targetObject, collator, nullptr, 2, String());
+        boundCompare = JSBoundFunction::create(vm, globalObject, targetObject, collator, nullptr, 2, jsEmptyString(vm));
         RETURN_IF_EXCEPTION(scope, encodedJSValue());
         // d. Set collator.[[boundCompare]] to bc.
         collator->setBoundCompare(vm, boundCompare);
index 1881e84..f67ca96 100644 (file)
@@ -143,7 +143,7 @@ EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeGetterFormat(JSGlobalObj
         // b. The value of F’s length property is 1. (Note: F’s length property was 0 in ECMA-402 1.0)
         JSFunction* targetObject = JSFunction::create(vm, globalObject, 1, "format"_s, IntlDateTimeFormatFuncFormatDateTime, NoIntrinsic);
         // c. Let bf be BoundFunctionCreate(F, «this value»).
-        boundFormat = JSBoundFunction::create(vm, globalObject, targetObject, dtf, nullptr, 1, String());
+        boundFormat = JSBoundFunction::create(vm, globalObject, targetObject, dtf, nullptr, 1, jsEmptyString(vm));
         RETURN_IF_EXCEPTION(scope, encodedJSValue());
         // d. Set dtf.[[boundFormat]] to bf.
         dtf->setBoundFormat(vm, boundFormat);
index 18b92b6..a3fdb6c 100644 (file)
@@ -136,7 +136,7 @@ EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeGetterFormat(JSGlobalObjec
         // b. The value of F’s length property is 1.
         JSFunction* targetObject = JSFunction::create(vm, globalObject, 1, "format"_s, IntlNumberFormatFuncFormatNumber, NoIntrinsic);
         // c. Let bf be BoundFunctionCreate(F, «this value»).
-        boundFormat = JSBoundFunction::create(vm, globalObject, targetObject, nf, nullptr, 1, String());
+        boundFormat = JSBoundFunction::create(vm, globalObject, targetObject, nf, nullptr, 1, jsEmptyString(vm));
         RETURN_IF_EXCEPTION(scope, encodedJSValue());
         // d. Set nf.[[boundFormat]] to bf.
         nf->setBoundFormat(vm, boundFormat);
index a41b57b..7f867f1 100644 (file)
@@ -187,8 +187,8 @@ const char* intrinsicName(Intrinsic intrinsic)
         return "TruncIntrinsic";
     case IsTypedArrayViewIntrinsic:
         return "IsTypedArrayViewIntrinsic";
-    case BoundThisNoArgsFunctionCallIntrinsic:
-        return "BoundThisNoArgsFunctionCallIntrinsic";
+    case BoundFunctionCallIntrinsic:
+        return "BoundFunctionCallIntrinsic";
     case JSMapGetIntrinsic:
         return "JSMapGetIntrinsic";
     case JSMapHasIntrinsic:
index fe2e910..bb2fee2 100644 (file)
@@ -106,7 +106,7 @@ enum Intrinsic : uint8_t {
     FRoundIntrinsic,
     TruncIntrinsic,
     IsTypedArrayViewIntrinsic,
-    BoundThisNoArgsFunctionCallIntrinsic,
+    BoundFunctionCallIntrinsic,
     JSMapGetIntrinsic,
     JSMapHasIntrinsic,
     JSMapSetIntrinsic,
index 75be5fe..418659a 100644 (file)
@@ -39,7 +39,14 @@ EncodedJSValue JSC_HOST_CALL boundThisNoArgsFunctionCall(JSGlobalObject* globalO
 {
     JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(callFrame->jsCallee());
 
+    JSImmutableButterfly* boundArgs = boundFunction->boundArgs();
+
     MarkedArgumentBuffer args;
+    if (boundArgs) {
+        // Starts with 1 since the first one is |this|.
+        for (unsigned i = 1; i < boundArgs->length(); ++i)
+            args.append(boundArgs->get(i));
+    }
     for (unsigned i = 0; i < callFrame->argumentCount(); ++i)
         args.append(callFrame->uncheckedArgument(i));
     RELEASE_ASSERT(!args.hasOverflowed());
@@ -62,12 +69,13 @@ EncodedJSValue JSC_HOST_CALL boundFunctionCall(JSGlobalObject* globalObject, Cal
     auto scope = DECLARE_THROW_SCOPE(vm);
     JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(callFrame->jsCallee());
 
-    JSArray* boundArgs = boundFunction->boundArgs();
+    JSImmutableButterfly* boundArgs = boundFunction->boundArgs();
 
     MarkedArgumentBuffer args;
     if (boundArgs) {
-        for (unsigned i = 0; i < boundArgs->length(); ++i)
-            args.append(boundArgs->getIndexQuickly(i));
+        // Starts with 1 since the first one is |this|.
+        for (unsigned i = 1; i < boundArgs->length(); ++i)
+            args.append(boundArgs->get(i));
     }
     for (unsigned i = 0; i < callFrame->argumentCount(); ++i)
         args.append(callFrame->uncheckedArgument(i));
@@ -87,7 +95,14 @@ EncodedJSValue JSC_HOST_CALL boundThisNoArgsFunctionConstruct(JSGlobalObject* gl
 {
     JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(callFrame->jsCallee());
 
+    JSImmutableButterfly* boundArgs = boundFunction->boundArgs();
+
     MarkedArgumentBuffer args;
+    if (boundArgs) {
+        // Starts with 1 since the first one is |this|.
+        for (unsigned i = 1; i < boundArgs->length(); ++i)
+            args.append(boundArgs->get(i));
+    }
     for (unsigned i = 0; i < callFrame->argumentCount(); ++i)
         args.append(callFrame->uncheckedArgument(i));
     RELEASE_ASSERT(!args.hasOverflowed());
@@ -105,12 +120,13 @@ EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(JSGlobalObject* globalObject
     auto scope = DECLARE_THROW_SCOPE(vm);
     JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(callFrame->jsCallee());
 
-    JSArray* boundArgs = boundFunction->boundArgs();
+    JSImmutableButterfly* boundArgs = boundFunction->boundArgs();
 
     MarkedArgumentBuffer args;
     if (boundArgs) {
-        for (unsigned i = 0; i < boundArgs->length(); ++i)
-            args.append(boundArgs->getIndexQuickly(i));
+        // Starts with 1 since the first one is |this|.
+        for (unsigned i = 1; i < boundArgs->length(); ++i)
+            args.append(boundArgs->get(i));
     }
     for (unsigned i = 0; i < callFrame->argumentCount(); ++i)
         args.append(callFrame->uncheckedArgument(i));
@@ -171,23 +187,22 @@ inline Structure* getBoundFunctionStructure(VM& vm, JSGlobalObject* globalObject
     return result;
 }
 
-JSBoundFunction* JSBoundFunction::create(VM& vm, JSGlobalObject* globalObject, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs, int length, const String& name)
+JSBoundFunction* JSBoundFunction::create(VM& vm, JSGlobalObject* globalObject, JSObject* targetFunction, JSValue boundThis, JSImmutableButterfly* boundArgs, int length, JSString* name)
 {
     auto scope = DECLARE_THROW_SCOPE(vm);
+
+    name->value(globalObject); // Resolving rope.
+    RETURN_IF_EXCEPTION(scope, nullptr);
+
+    bool isJSFunction = getJSFunction(targetFunction);
     ConstructData constructData;
     ConstructType constructType = JSC::getConstructData(vm, targetFunction, constructData);
     bool canConstruct = constructType != ConstructType::None;
-    
-    bool slowCase = boundArgs || !getJSFunction(targetFunction);
-    
-    NativeExecutable* executable = vm.getHostFunction(
-        slowCase ? boundFunctionCall : boundThisNoArgsFunctionCall,
-        slowCase ? NoIntrinsic : BoundThisNoArgsFunctionCallIntrinsic,
-        canConstruct ? (slowCase ? boundFunctionConstruct : boundThisNoArgsFunctionConstruct) : callHostFunctionAsConstructor, nullptr,
-        name);
+
+    NativeExecutable* executable = vm.getBoundFunction(isJSFunction, canConstruct);
     Structure* structure = getBoundFunctionStructure(vm, globalObject, targetFunction);
     RETURN_IF_EXCEPTION(scope, nullptr);
-    JSBoundFunction* function = new (NotNull, allocateCell<JSBoundFunction>(vm.heap)) JSBoundFunction(vm, globalObject, structure, targetFunction, boundThis, boundArgs);
+    JSBoundFunction* function = new (NotNull, allocateCell<JSBoundFunction>(vm.heap)) JSBoundFunction(vm, globalObject, structure, targetFunction, boundThis, boundArgs, name, length);
 
     function->finishCreation(vm, executable, length);
     return function;
@@ -198,12 +213,15 @@ bool JSBoundFunction::customHasInstance(JSObject* object, JSGlobalObject* global
     return jsCast<JSBoundFunction*>(object)->m_targetFunction->hasInstance(globalObject, value);
 }
 
-JSBoundFunction::JSBoundFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs)
+JSBoundFunction::JSBoundFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* targetFunction, JSValue boundThis, JSImmutableButterfly* boundArgs, JSString* name, int length)
     : Base(vm, globalObject, structure)
     , m_targetFunction(vm, this, targetFunction)
     , m_boundThis(vm, this, boundThis)
-    , m_boundArgs(vm, this, boundArgs, WriteBarrier<JSArray>::MayBeNull)
+    , m_boundArgs(vm, this, boundArgs, WriteBarrier<JSImmutableButterfly>::MayBeNull)
+    , m_name(vm, this, name)
+    , m_length(length)
 {
+    ASSERT(!m_name->isRope());
 }
 
 JSArray* JSBoundFunction::boundArgsCopy(JSGlobalObject* globalObject)
@@ -212,9 +230,12 @@ JSArray* JSBoundFunction::boundArgsCopy(JSGlobalObject* globalObject)
     auto scope = DECLARE_THROW_SCOPE(vm);
     JSArray* result = constructEmptyArray(this->globalObject(), nullptr);
     RETURN_IF_EXCEPTION(scope, nullptr);
-    for (unsigned i = 0; i < m_boundArgs->length(); ++i) {
-        result->push(globalObject, m_boundArgs->getIndexQuickly(i));
-        RETURN_IF_EXCEPTION(scope, nullptr);
+    if (m_boundArgs) {
+        // Starts with 1 since the first one is bound |this|.
+        for (unsigned i = 1; i < m_boundArgs->length(); ++i) {
+            result->push(globalObject, m_boundArgs->get(i));
+            RETURN_IF_EXCEPTION(scope, nullptr);
+        }
     }
     return result;
 }
@@ -235,6 +256,7 @@ void JSBoundFunction::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(thisObject->m_targetFunction);
     visitor.append(thisObject->m_boundThis);
     visitor.append(thisObject->m_boundArgs);
+    visitor.append(thisObject->m_name);
 }
 
 } // namespace JSC
index 7677c6a..73b0145 100644 (file)
@@ -48,14 +48,23 @@ public:
         return vm.boundFunctionSpace<mode>();
     }
 
-    static JSBoundFunction* create(VM&, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs, int, const String& name);
+    static JSBoundFunction* create(VM&, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSImmutableButterfly* boundArgs, int, JSString* name);
     
     static bool customHasInstance(JSObject*, JSGlobalObject*, JSValue);
 
     JSObject* targetFunction() { return m_targetFunction.get(); }
     JSValue boundThis() { return m_boundThis.get(); }
-    JSArray* boundArgs() { return m_boundArgs.get(); } // DO NOT allow this array to be mutated!
+    JSImmutableButterfly* boundArgs() { return m_boundArgs.get(); } // DO NOT allow this array to be mutated!
     JSArray* boundArgsCopy(JSGlobalObject*);
+    JSString* name() { return m_name.get(); }
+    const String& nameString()
+    {
+        ASSERT(!m_name->isRope());
+        bool allocationAllowed = false;
+        return m_name->tryGetValue(allocationAllowed);
+    }
+
+    int32_t length(VM&) { return m_length; }
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
@@ -65,6 +74,7 @@ public:
     
     static ptrdiff_t offsetOfTargetFunction() { return OBJECT_OFFSETOF(JSBoundFunction, m_targetFunction); }
     static ptrdiff_t offsetOfBoundThis() { return OBJECT_OFFSETOF(JSBoundFunction, m_boundThis); }
+    static ptrdiff_t offsetOfBoundArgs() { return OBJECT_OFFSETOF(JSBoundFunction, m_boundArgs); }
 
     DECLARE_INFO;
 
@@ -72,13 +82,20 @@ protected:
     static void visitChildren(JSCell*, SlotVisitor&);
 
 private:
-    JSBoundFunction(VM&, JSGlobalObject*, Structure*, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs);
+    JSBoundFunction(VM&, JSGlobalObject*, Structure*, JSObject* targetFunction, JSValue boundThis, JSImmutableButterfly* boundArgs, JSString* name, int length);
     
     void finishCreation(VM&, NativeExecutable*, int length);
 
     WriteBarrier<JSObject> m_targetFunction;
     WriteBarrier<Unknown> m_boundThis;
-    WriteBarrier<JSArray> m_boundArgs;
+    WriteBarrier<JSImmutableButterfly> m_boundArgs;
+    WriteBarrier<JSString> m_name;
+    int m_length;
 };
 
+EncodedJSValue JSC_HOST_CALL boundFunctionCall(JSGlobalObject*, CallFrame*);
+EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(JSGlobalObject*, CallFrame*);
+EncodedJSValue JSC_HOST_CALL boundThisNoArgsFunctionCall(JSGlobalObject*, CallFrame*);
+EncodedJSValue JSC_HOST_CALL boundThisNoArgsFunctionConstruct(JSGlobalObject*, CallFrame*);
+
 } // namespace JSC
index f7e01d4..ba9860c 100644 (file)
@@ -126,6 +126,8 @@ void JSFunction::finishCreation(VM& vm)
     Base::finishCreation(vm);
     ASSERT(jsDynamicCast<JSFunction*>(vm, this));
     ASSERT(type() == JSFunctionType);
+    ASSERT(methodTable(vm)->getConstructData == &JSFunction::getConstructData);
+    ASSERT(methodTable(vm)->getCallData == &JSFunction::getCallData);
 }
 
 void JSFunction::finishCreation(VM& vm, NativeExecutable* executable, int length, const String& name)
@@ -133,8 +135,14 @@ void JSFunction::finishCreation(VM& vm, NativeExecutable* executable, int length
     Base::finishCreation(vm);
     ASSERT(inherits(vm, info()));
     ASSERT(type() == JSFunctionType);
+    ASSERT(methodTable(vm)->getConstructData == &JSFunction::getConstructData);
+    ASSERT(methodTable(vm)->getCallData == &JSFunction::getCallData);
     m_executable.set(vm, this, executable);
-    // Some NativeExecutable functions, like JSBoundFunction, decide to lazily allocate their name string.
+
+    // Some NativeExecutable functions, like JSBoundFunction, decide to lazily allocate their name string / length.
+    if (this->inherits<JSBoundFunction>(vm))
+        return;
+
     if (!name.isNull())
         putDirect(vm, vm.propertyNames->name, jsString(vm, name), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
     putDirect(vm, vm.propertyNames->length, jsNumber(length), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
@@ -207,6 +215,8 @@ FunctionRareData* JSFunction::initializeRareData(JSGlobalObject* globalObject, s
 String JSFunction::name(VM& vm)
 {
     if (isHostFunction()) {
+        if (this->inherits<JSBoundFunction>(vm))
+            return jsCast<JSBoundFunction*>(this)->nameString();
         NativeExecutable* executable = jsCast<NativeExecutable*>(this->executable());
         return executable->name();
     }
@@ -421,10 +431,13 @@ EncodedJSValue JSFunction::callerGetter(JSGlobalObject* globalObject, EncodedJSV
 bool JSFunction::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot)
 {
     VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSFunction* thisObject = jsCast<JSFunction*>(object);
     if (thisObject->isHostOrBuiltinFunction()) {
         thisObject->reifyLazyPropertyForHostOrBuiltinIfNeeded(vm, globalObject, propertyName);
-        return Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot);
+        RETURN_IF_EXCEPTION(scope, false);
+        RELEASE_AND_RETURN(scope, Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot));
     }
 
     if (propertyName == vm.propertyNames->prototype && thisObject->jsExecutable()->hasPrototypeProperty() && !thisObject->jsExecutable()->isClassConstructorFunction()) {
@@ -460,22 +473,23 @@ bool JSFunction::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObje
 
     if (propertyName == vm.propertyNames->arguments) {
         if (!thisObject->jsExecutable()->hasCallerAndArgumentsProperties())
-            return Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot);
+            RELEASE_AND_RETURN(scope, Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot));
         
         slot.setCacheableCustom(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete, argumentsGetter);
         return true;
 
     } else if (propertyName == vm.propertyNames->caller) {
         if (!thisObject->jsExecutable()->hasCallerAndArgumentsProperties())
-            return Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot);
+            RELEASE_AND_RETURN(scope, Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot));
 
         slot.setCacheableCustom(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete, callerGetter);
         return true;
     }
 
     thisObject->reifyLazyPropertyIfNeeded(vm, globalObject, propertyName);
+    RETURN_IF_EXCEPTION(scope, false);
 
-    return Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot);
+    RELEASE_AND_RETURN(scope, Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot));
 }
 
 void JSFunction::getOwnNonIndexPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, EnumerationMode mode)
@@ -497,10 +511,12 @@ void JSFunction::getOwnNonIndexPropertyNames(JSObject* object, JSGlobalObject* g
             if (!thisObject->hasReifiedName())
                 propertyNames.add(vm.propertyNames->name);
         } else {
-            if (thisObject->isBuiltinFunction() && !thisObject->hasReifiedLength())
-                propertyNames.add(vm.propertyNames->length);
-            if ((thisObject->isBuiltinFunction() || thisObject->inherits<JSBoundFunction>(vm)) && !thisObject->hasReifiedName())
-                propertyNames.add(vm.propertyNames->name);
+            if (thisObject->isBuiltinFunction() || thisObject->inherits<JSBoundFunction>(vm)) {
+                if (!thisObject->hasReifiedLength())
+                    propertyNames.add(vm.propertyNames->length);
+                if (!thisObject->hasReifiedName())
+                    propertyNames.add(vm.propertyNames->name);
+            }
         }
     }
     Base::getOwnNonIndexPropertyNames(thisObject, globalObject, propertyNames, mode);
@@ -513,12 +529,21 @@ bool JSFunction::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName pr
 
     JSFunction* thisObject = jsCast<JSFunction*>(cell);
 
+    if (propertyName == vm.propertyNames->length || propertyName == vm.propertyNames->name) {
+        FunctionRareData* rareData = thisObject->rareData(vm);
+        if (propertyName == vm.propertyNames->length)
+            rareData->setHasModifiedLength();
+        else
+            rareData->setHasModifiedName();
+    }
+
     if (UNLIKELY(isThisValueAltered(slot, thisObject)))
         RELEASE_AND_RETURN(scope, ordinarySetSlow(globalObject, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode()));
 
 
     if (thisObject->isHostOrBuiltinFunction()) {
         PropertyStatus propertyType = thisObject->reifyLazyPropertyForHostOrBuiltinIfNeeded(vm, globalObject, propertyName);
+        RETURN_IF_EXCEPTION(scope, false);
         if (isLazy(propertyType))
             slot.disableCaching();
         RELEASE_AND_RETURN(scope, Base::put(thisObject, globalObject, propertyName, value, slot));
@@ -551,10 +576,21 @@ bool JSFunction::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName pr
 bool JSFunction::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName)
 {
     VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
     JSFunction* thisObject = jsCast<JSFunction*>(cell);
-    if (thisObject->isHostOrBuiltinFunction())
+
+    if (propertyName == vm.propertyNames->length || propertyName == vm.propertyNames->name) {
+        FunctionRareData* rareData = thisObject->rareData(vm);
+        if (propertyName == vm.propertyNames->length)
+            rareData->setHasModifiedLength();
+        else
+            rareData->setHasModifiedName();
+    }
+
+    if (thisObject->isHostOrBuiltinFunction()) {
         thisObject->reifyLazyPropertyForHostOrBuiltinIfNeeded(vm, globalObject, propertyName);
-    else if (vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable) {
+        RETURN_IF_EXCEPTION(scope, false);
+    } else if (vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable) {
         // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty.
         FunctionExecutable* executable = thisObject->jsExecutable();
         
@@ -565,9 +601,10 @@ bool JSFunction::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, Prop
             return false;
 
         thisObject->reifyLazyPropertyIfNeeded(vm, globalObject, propertyName);
+        RETURN_IF_EXCEPTION(scope, false);
     }
     
-    return Base::deleteProperty(thisObject, globalObject, propertyName);
+    RELEASE_AND_RETURN(scope, Base::deleteProperty(thisObject, globalObject, propertyName));
 }
 
 bool JSFunction::defineOwnProperty(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
@@ -576,8 +613,18 @@ bool JSFunction::defineOwnProperty(JSObject* object, JSGlobalObject* globalObjec
     auto scope = DECLARE_THROW_SCOPE(vm);
 
     JSFunction* thisObject = jsCast<JSFunction*>(object);
+
+    if (propertyName == vm.propertyNames->length || propertyName == vm.propertyNames->name) {
+        FunctionRareData* rareData = thisObject->rareData(vm);
+        if (propertyName == vm.propertyNames->length)
+            rareData->setHasModifiedLength();
+        else
+            rareData->setHasModifiedName();
+    }
+
     if (thisObject->isHostOrBuiltinFunction()) {
         thisObject->reifyLazyPropertyForHostOrBuiltinIfNeeded(vm, globalObject, propertyName);
+        RETURN_IF_EXCEPTION(scope, false);
         RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, throwException));
     }
 
@@ -717,8 +764,14 @@ void JSFunction::reifyLength(VM& vm)
     FunctionRareData* rareData = this->rareData(vm);
 
     ASSERT(!hasReifiedLength());
-    ASSERT(!isHostFunction());
-    JSValue initialValue = jsNumber(jsExecutable()->parameterCount());
+    unsigned length = 0;
+    if (this->inherits<JSBoundFunction>(vm))
+        length = jsCast<JSBoundFunction*>(this)->length(vm);
+    else {
+        ASSERT(!isHostFunction());
+        length = jsExecutable()->parameterCount();
+    }
+    JSValue initialValue = jsNumber(length);
     unsigned initialAttributes = PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
     const Identifier& identifier = vm.propertyNames->length;
     rareData->setHasReifiedLength();
@@ -767,7 +820,7 @@ void JSFunction::reifyName(VM& vm, JSGlobalObject* globalObject, String name)
 
 JSFunction::PropertyStatus JSFunction::reifyLazyPropertyIfNeeded(VM& vm, JSGlobalObject* globalObject, PropertyName propertyName)
 {
-    if (isHostOrBuiltinFunction())
+    if (isHostOrBuiltinFunction() && !this->inherits<JSBoundFunction>(vm))
         return PropertyStatus::Eager;
     PropertyStatus lazyLength = reifyLazyLengthIfNeeded(vm, globalObject, propertyName);
     if (isLazy(lazyLength))
@@ -781,7 +834,7 @@ JSFunction::PropertyStatus JSFunction::reifyLazyPropertyIfNeeded(VM& vm, JSGloba
 JSFunction::PropertyStatus JSFunction::reifyLazyPropertyForHostOrBuiltinIfNeeded(VM& vm, JSGlobalObject* globalObject, PropertyName propertyName)
 {
     ASSERT(isHostOrBuiltinFunction());
-    if (isBuiltinFunction()) {
+    if (isBuiltinFunction() || this->inherits<JSBoundFunction>(vm)) {
         PropertyStatus lazyLength = reifyLazyLengthIfNeeded(vm, globalObject, propertyName);
         if (isLazy(lazyLength))
             return lazyLength;
@@ -815,6 +868,8 @@ JSFunction::PropertyStatus JSFunction::reifyLazyNameIfNeeded(VM& vm, JSGlobalObj
 
 JSFunction::PropertyStatus JSFunction::reifyLazyBoundNameIfNeeded(VM& vm, JSGlobalObject* globalObject, PropertyName propertyName)
 {
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     const Identifier& nameIdent = vm.propertyNames->name;
     if (propertyName != nameIdent)
         return PropertyStatus::Eager;
@@ -826,8 +881,13 @@ JSFunction::PropertyStatus JSFunction::reifyLazyBoundNameIfNeeded(VM& vm, JSGlob
         reifyName(vm, globalObject);
     else if (this->inherits<JSBoundFunction>(vm)) {
         FunctionRareData* rareData = this->rareData(vm);
-        const String& name = static_cast<NativeExecutable*>(m_executable.get())->name();
-        JSString* string = !name ? jsEmptyString(vm) : jsString(vm, makeString("bound ", name));
+        JSString* name = jsCast<JSBoundFunction*>(this)->name();
+        JSString* string = nullptr;
+        if (name->length() != 0) {
+            string = jsString(globalObject, jsString(vm, "bound "_s), name);
+            RETURN_IF_EXCEPTION(scope, PropertyStatus::Lazy);
+        } else
+            string = jsString(vm, "bound "_s);
         unsigned initialAttributes = PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
         rareData->setHasReifiedName();
         putDirect(vm, nameIdent, string, initialAttributes);
index 6e88376..dc05720 100644 (file)
@@ -111,8 +111,8 @@ public:
     TaggedNativeFunction nativeFunction();
     TaggedNativeFunction nativeConstructor();
 
-    static ConstructType getConstructData(JSCell*, ConstructData&);
-    static CallType getCallData(JSCell*, CallData&);
+    JS_EXPORT_PRIVATE static ConstructType getConstructData(JSCell*, ConstructData&);
+    JS_EXPORT_PRIVATE static CallType getCallData(JSCell*, CallData&);
 
     static inline ptrdiff_t offsetOfExecutable()
     {
@@ -164,6 +164,8 @@ public:
     };
     PropertyStatus reifyLazyPropertyIfNeeded(VM&, JSGlobalObject*, PropertyName);
 
+    bool areNameAndLengthOriginal(VM&);
+
 protected:
     JS_EXPORT_PRIVATE JSFunction(VM&, JSGlobalObject*, Structure*);
     JSFunction(VM&, FunctionExecutable*, JSScope*, Structure*);
index 4c14560..b34c16b 100644 (file)
@@ -108,6 +108,17 @@ inline bool JSFunction::hasReifiedName() const
     return m_rareData && m_rareData->hasReifiedName();
 }
 
+inline bool JSFunction::areNameAndLengthOriginal(VM&)
+{
+    if (!m_rareData)
+        return true;
+    if (m_rareData->hasModifiedName())
+        return false;
+    if (m_rareData->hasModifiedLength())
+        return false;
+    return true;
+}
+
 inline bool JSFunction::canUseAllocationProfile()
 {
     if (isHostOrBuiltinFunction()) {
index f59c7ac..247607c 100644 (file)
@@ -285,16 +285,17 @@ static EncodedJSValue JSC_HOST_CALL makeBoundFunction(JSGlobalObject* globalObje
     ASSERT(lengthValue.isInt32AsAnyInt());
     int32_t length = lengthValue.asInt32AsAnyInt();
 
-    String name = nameString->value(globalObject);
-    RETURN_IF_EXCEPTION(scope, { });
-
-    RELEASE_AND_RETURN(scope, JSValue::encode(JSBoundFunction::create(vm, globalObject, target, boundThis, boundArgs.isCell() ? jsCast<JSArray*>(boundArgs) : nullptr, length, WTFMove(name))));
+    RELEASE_AND_RETURN(scope, JSValue::encode(JSBoundFunction::create(vm, globalObject, target, boundThis, boundArgs.isCell() ? jsCast<JSImmutableButterfly*>(boundArgs) : nullptr, length, nameString)));
 }
 
 static EncodedJSValue JSC_HOST_CALL hasOwnLengthProperty(JSGlobalObject* globalObject, CallFrame* callFrame)
 {
     VM& vm = globalObject->vm();
+
     JSObject* target = asObject(callFrame->uncheckedArgument(0));
+    JSFunction* function = jsDynamicCast<JSFunction*>(vm, target);
+    if (function)
+        return JSValue::encode(jsBoolean(function->areNameAndLengthOriginal(vm)));
     return JSValue::encode(jsBoolean(target->hasOwnProperty(globalObject, vm.propertyNames->length)));
 }
 
index b7db454..ff04f59 100644 (file)
@@ -55,7 +55,7 @@ inline JSCell* getJSFunction(JSValue value)
 {
     if (value.isCell() && (value.asCell()->type() == JSFunctionType))
         return value.asCell();
-    return 0;
+    return nullptr;
 }
 
 class Exception;
@@ -1292,20 +1292,6 @@ inline void JSObject::nukeStructureAndSetButterfly(VM& vm, StructureID oldStruct
     m_butterfly.set(vm, this, butterfly);
 }
 
-inline CallType getCallData(VM& vm, JSValue value, CallData& callData)
-{
-    CallType result = value.isCell() ? value.asCell()->methodTable(vm)->getCallData(value.asCell(), callData) : CallType::None;
-    ASSERT(result == CallType::None || value.isValidCallee());
-    return result;
-}
-
-inline ConstructType getConstructData(VM& vm, JSValue value, ConstructData& constructData)
-{
-    ConstructType result = value.isCell() ? value.asCell()->methodTable(vm)->getConstructData(value.asCell(), constructData) : ConstructType::None;
-    ASSERT(result == ConstructType::None || value.isValidCallee());
-    return result;
-}
-
 inline JSObject* asObject(JSCell* cell)
 {
     ASSERT(cell->isObject());
index 07f6064..06a714f 100644 (file)
@@ -26,6 +26,7 @@
 #include "AuxiliaryBarrierInlines.h"
 #include "ButterflyInlines.h"
 #include "Error.h"
+#include "JSFunction.h"
 #include "JSObject.h"
 #include "JSTypedArrays.h"
 #include "Lookup.h"
@@ -510,4 +511,28 @@ inline bool JSObject::putOwnDataPropertyMayBeIndex(JSGlobalObject* globalObject,
     return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot);
 }
 
+inline CallType getCallData(VM& vm, JSValue value, CallData& callData)
+{
+    if (!value.isCell())
+        return CallType::None;
+    JSCell* cell = value.asCell();
+    if (cell->type() == JSFunctionType)
+        return JSFunction::getCallData(cell, callData);
+    CallType result = cell->methodTable(vm)->getCallData(cell, callData);
+    ASSERT(result == CallType::None || value.isValidCallee());
+    return result;
+}
+
+inline ConstructType getConstructData(VM& vm, JSValue value, ConstructData& constructData)
+{
+    if (!value.isCell())
+        return ConstructType::None;
+    JSCell* cell = value.asCell();
+    if (cell->type() == JSFunctionType)
+        return JSFunction::getConstructData(cell, constructData);
+    ConstructType result = cell->methodTable(vm)->getConstructData(cell, constructData);
+    ASSERT(result == ConstructType::None || value.isValidCallee());
+    return result;
+}
+
 } // namespace JSC
index 64ddfcb..ea3b493 100644 (file)
@@ -766,8 +766,8 @@ static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic)
         return imulThunkGenerator;
     case RandomIntrinsic:
         return randomThunkGenerator;
-    case BoundThisNoArgsFunctionCallIntrinsic:
-        return boundThisNoArgsFunctionCallGenerator;
+    case BoundFunctionCallIntrinsic:
+        return boundFunctionCallGenerator;
     default:
         return nullptr;
     }
@@ -815,6 +815,31 @@ NativeExecutable* VM::getHostFunction(NativeFunction function, Intrinsic intrins
     return NativeExecutable::create(*this, jitCodeForCallTrampoline(), function, jitCodeForConstructTrampoline(), constructor, name);
 }
 
+NativeExecutable* VM::getBoundFunction(bool isJSFunction, bool canConstruct)
+{
+    bool slowCase = !isJSFunction;
+
+    auto getOrCreate = [&] (Weak<NativeExecutable>& slot) -> NativeExecutable* {
+        if (auto* cached = slot.get())
+            return cached;
+        NativeExecutable* result = getHostFunction(
+            slowCase ? boundFunctionCall : boundThisNoArgsFunctionCall,
+            slowCase ? NoIntrinsic : BoundFunctionCallIntrinsic,
+            canConstruct ? (slowCase ? boundFunctionConstruct : boundThisNoArgsFunctionConstruct) : callHostFunctionAsConstructor, nullptr, String());
+        slot = Weak<NativeExecutable>(result);
+        return result;
+    };
+
+    if (slowCase) {
+        if (canConstruct)
+            return getOrCreate(m_slowCanConstructBoundExecutable);
+        return getOrCreate(m_slowBoundExecutable);
+    }
+    if (canConstruct)
+        return getOrCreate(m_fastCanConstructBoundExecutable);
+    return getOrCreate(m_fastBoundExecutable);
+}
+
 MacroAssemblerCodePtr<JSEntryPtrTag> VM::getCTIInternalFunctionTrampolineFor(CodeSpecializationKind kind)
 {
 #if ENABLE(JIT)
index 28086c4..539513b 100644 (file)
@@ -694,6 +694,11 @@ public:
     Strong<JSCell> m_sentinelSetBucket;
     Strong<JSCell> m_sentinelMapBucket;
 
+    Weak<NativeExecutable> m_fastBoundExecutable;
+    Weak<NativeExecutable> m_fastCanConstructBoundExecutable;
+    Weak<NativeExecutable> m_slowBoundExecutable;
+    Weak<NativeExecutable> m_slowCanConstructBoundExecutable;
+
     Ref<PromiseTimer> promiseTimer;
     
     JSCell* currentlyDestructingCallbackObject;
@@ -831,6 +836,8 @@ public:
     NativeExecutable* getHostFunction(NativeFunction, NativeFunction constructor, const String& name);
     NativeExecutable* getHostFunction(NativeFunction, Intrinsic, NativeFunction constructor, const DOMJIT::Signature*, const String& name);
 
+    NativeExecutable* getBoundFunction(bool isJSFunction, bool canConstruct);
+
     MacroAssemblerCodePtr<JSEntryPtrTag> getCTIInternalFunctionTrampolineFor(CodeSpecializationKind);
 
     static ptrdiff_t exceptionOffset()
index b267d51..677e885 100644 (file)
@@ -263,7 +263,7 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM& vm
     materializeImportJSCell(jit, importIndex, importJSCellGPRReg);
 
     jit.store64(importJSCellGPRReg, calleeFrame.withOffset(CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
-    jit.store32(JIT::TrustedImm32(numberOfParameters), calleeFrame.withOffset(CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset));
+    jit.store32(JIT::TrustedImm32(numberOfParameters), calleeFrame.withOffset(CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset));
     jit.store64(JIT::TrustedImm64(JSValue::ValueUndefined), calleeFrame.withOffset(CallFrameSlot::thisArgument * static_cast<int>(sizeof(Register))));
 
     // FIXME Tail call if the wasm return type is void and no registers were spilled. https://bugs.webkit.org/show_bug.cgi?id=165488
index a4973eb..52b99c4 100644 (file)
@@ -243,7 +243,7 @@ MacroAssemblerCodePtr<JSEntryPtrTag> WebAssemblyFunction::jsCallEntrypointSlow()
     // FIXME: We should handle mismatched arity
     // https://bugs.webkit.org/show_bug.cgi?id=196564
     slowPath.append(jit.branch32(CCallHelpers::Below,
-        CCallHelpers::payloadFor(CallFrameSlot::argumentCount), CCallHelpers::TrustedImm32(signature.argumentCount() + 1)));
+        CCallHelpers::payloadFor(CallFrameSlot::argumentCountIncludingThis), CCallHelpers::TrustedImm32(signature.argumentCount() + 1)));
 
     if (useTagRegisters())
         jit.emitMaterializeTagCheckRegisters();
index 6a53afc..e245471 100644 (file)
@@ -1,3 +1,14 @@
+2019-12-21  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Improve our bound function implementation
+        https://bugs.webkit.org/show_bug.cgi?id=205327
+
+        Reviewed by Keith Miller.
+
+        Support running slow-microbenchmarks.
+
+        * Scripts/run-jsc-benchmarks:
+
 2019-12-21  Kate Cheney  <katherine_cheney@apple.com>
 
         Add timeStamp to ITP database
index 2ccfe92..f4306dc 100755 (executable)
@@ -51,6 +51,7 @@ V8_PATH = PERFORMANCETESTS_PATH + "SunSpider" + "tests" + "v8-v6"
 TAILBENCH_PATH = PERFORMANCETESTS_PATH + "TailBench9000"
 BIGINTBENCH_PATH = PERFORMANCETESTS_PATH + "BigIntBench"
 MICROBENCHMARKS_PATH = OPENSOURCE_PATH + "JSTests" + "microbenchmarks"
+SLOW_MICROBENCHMARKS_PATH = OPENSOURCE_PATH + "JSTests" + "slowMicrobenchmarks"
 OPENSOURCE_OCTANE_PATH = PERFORMANCETESTS_PATH + "Octane"
 OCTANE_WRAPPER_PATH = OPENSOURCE_OCTANE_PATH + "wrappers"
 JSBENCH_PATH = PERFORMANCETESTS_PATH + "JSBench"
@@ -224,6 +225,7 @@ $includeV8CompileTime=true
 $includeKraken=true
 $includeJSBench=true
 $includeMicrobenchmarks=true
+$includeSlowMicrobenchmarks=true
 $includeAsmBench=true
 $includeDSPJS=true
 $includeBrowsermarkJS=false
@@ -323,6 +325,8 @@ def usage
   puts "--kraken             Only run Kraken."
   puts "--js-bench           Only run JSBench."
   puts "--microbenchmarks    Only run microbenchmarks."
+  puts "--slow-microbenchmarks"
+  puts "                     Only run slow-microbenchmarks."
   puts "--dsp                Only run DSP."
   puts "--asm-bench          Only run AsmBench."
   puts "--browsermark-js     Only run browsermark-js."
@@ -1776,6 +1780,18 @@ class MicrobenchmarksBenchmark
   end
 end
 
+class SlowMicrobenchmarksBenchmark
+  include Benchmark
+  
+  def initialize(name)
+    @name = name
+  end
+  
+  def emitRunCode(plan)
+    emitBenchRunCode(fullname, plan, SingleFileTimedBenchmarkParameters.new(ensureFile("SlowMicrobenchmarks-#{@name}", "#{SLOW_MICROBENCHMARKS_PATH}/#{@name}.js")))
+  end
+end
+
 class AsmBenchBenchmark
   include Benchmark
   
@@ -2811,6 +2827,7 @@ begin
       $includeKraken = false
       $includeJSBench = false
       $includeMicrobenchmarks = false
+      $includeSlowMicrobenchmarks = false
       $includeAsmBench = false
       $includeDSPJS = false
       $includeBrowsermarkJS = false
@@ -2839,6 +2856,7 @@ begin
                  ['--kraken', GetoptLong::NO_ARGUMENT],
                  ['--js-bench', GetoptLong::NO_ARGUMENT],
                  ['--microbenchmarks', GetoptLong::NO_ARGUMENT],
+                 ['--slow-microbenchmarks', GetoptLong::NO_ARGUMENT],
                  ['--asm-bench', GetoptLong::NO_ARGUMENT],
                  ['--dsp', GetoptLong::NO_ARGUMENT],
                  ['--browsermark-js', GetoptLong::NO_ARGUMENT],
@@ -2939,6 +2957,9 @@ begin
     when '--microbenchmarks'
       resetBenchOptionsIfNecessary
       $includeMicrobenchmarks = true
+    when '--slow-microbenchmarks'
+      resetBenchOptionsIfNecessary
+      $includeSlowMicrobenchmarks = true
     when '--asm-bench'
       resetBenchOptionsIfNecessary
       $includeAsmBench = true
@@ -3208,6 +3229,15 @@ begin
       MICROBENCHMARKS.add MicrobenchmarksBenchmark.new(name)
     end
   }
+
+  SLOW_MICROBENCHMARKS = BenchmarkSuite.new("SlowMicrobenchmarks", :geometricMean, 0)
+  Dir.foreach(SLOW_MICROBENCHMARKS_PATH) {
+    | filename |
+    if filename =~ /\.js$/
+      name = $~.pre_match
+      SLOW_MICROBENCHMARKS.add SlowMicrobenchmarksBenchmark.new(name)
+    end
+  }
   
   ASMBENCH = BenchmarkSuite.new("AsmBench", :geometricMean, 0)
   if ASMBENCH_PATH
@@ -3354,6 +3384,10 @@ begin
     $suites << MICROBENCHMARKS
   end
 
+  if $includeSlowMicrobenchmarks and not SLOW_MICROBENCHMARKS.empty?
+    $suites << SLOW_MICROBENCHMARKS
+  end
+
   if $includeBigIntBench and not BIGINTBENCH.empty?
     $suites << BIGINTBENCH
   end