ASSIGNED 190668
Introduce ReflectApplyFunctionCallDotNode to optimize Reflect.apply
https://bugs.webkit.org/show_bug.cgi?id=190668
Summary Introduce ReflectApplyFunctionCallDotNode to optimize Reflect.apply
Mark Lam
Reported 2018-10-17 10:33:51 PDT
Currently, it only checks for Function.apply and actually slows down the call to Reflect.apply. We should either provide an optimized Reflect.apply or remove the check against Function.apply when we detect Reflect.apply.
Attachments
Patch (25.55 KB, patch)
2020-07-13 15:22 PDT, Alexey Shvayka
no flags
Patch (35.20 KB, patch)
2020-07-22 07:38 PDT, Alexey Shvayka
no flags
Patch (33.55 KB, patch)
2020-09-09 12:04 PDT, Alexey Shvayka
no flags
Patch (34.64 KB, patch)
2020-09-09 16:25 PDT, Alexey Shvayka
saam: review+
Radar WebKit Bug Importer
Comment 1 2018-10-17 10:34:35 PDT
Alexey Shvayka
Comment 2 2020-07-13 15:22:15 PDT
Alexey Shvayka
Comment 3 2020-07-13 15:23:13 PDT
(In reply to Alexey Shvayka from comment #2) > Created attachment 404186 [details] > Patch Warmed-up runs, --outer 25: r264266 patch reflect-apply-array-literal 273.3377+-18.5141 ^ 18.7225+-0.5229 ^ definitely 14.5994x faster reflect-apply-spread 78.0319+-1.9222 ^ 57.0211+-1.0070 ^ definitely 1.3685x faster reflect-apply 76.8752+-10.2207 ^ 57.1569+-0.5991 ^ definitely 1.3450x faster <geometric> 117.0257+-4.6683 ^ 39.3459+-0.4078 ^ definitely 2.9743x faster
Saam Barati
Comment 4 2020-07-21 12:03:43 PDT
Comment on attachment 404186 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=404186&action=review > Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:2041 > + // Reflect.apply(...args) > + SpreadExpressionNode* spread = static_cast<SpreadExpressionNode*>(listNode->m_expr); > + RefPtr<RegisterID> spreadRegister = generator.emitNode(spread->expression()); > + generator.emitExpressionInfo(spread->divot(), spread->divotStart(), spread->divotEnd()); > + RefPtr<RegisterID> functionRegister = generator.emitGetByVal(generator.newTemporary(), spreadRegister.get(), generator.emitLoad(nullptr, jsNumber(0))); > + RefPtr<RegisterID> thisRegister = generator.emitGetByVal(generator.newTemporary(), spreadRegister.get(), generator.emitLoad(nullptr, jsNumber(1))); > + RefPtr<RegisterID> argumentsRegister = generator.emitGetByVal(generator.newTemporary(), spreadRegister.get(), generator.emitLoad(nullptr, jsNumber(2))); > + > + emitIsObject(argumentsRegister.get()); > + generator.emitCallVarargsInTailPosition(returnValue.get(), functionRegister.get(), thisRegister.get(), argumentsRegister.get(), generator.newTemporary(), 0, divot(), divotStart(), divotEnd(), DebuggableCall::Yes); > + generator.emitJump(end.get()); how is this spec compliant? Don't we need to run the iterator protocol?
Mark Lam
Comment 5 2020-07-21 12:24:08 PDT
Comment on attachment 404186 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=404186&action=review > JSTests/stress/reflect-apply.js:140 > + var args = [1, 2]; The spec (https://tc39.es/ecma262/#sec-reflect.apply) says that the 3rd argument can be anything that is array-like. Can you also add cases for object that are array-like e.g. the arguments object in a function, a plain old object with indexed properties, a proxy of an array with a get handler. Other interesting cases to test with: an array with a few holes in the front or middle, an array with a trailing comma. Also, in a separate file, add a case for an object with an indexed property in its prototype. You'll want a different test file because the "object with an indexed property in its prototype" case will put the VM into a "haveABadTime" state. You can also keep all the other test cases to run in this state. You can call this other test file reflect-apply-while-having-a-bad-time.js
Alexey Shvayka
Comment 6 2020-07-22 07:38:06 PDT
Created attachment 404922 [details] Patch Add more test cases and reflect-apply-while-having-a-bad-time.js.
Alexey Shvayka
Comment 7 2020-07-22 07:46:41 PDT
(In reply to Saam Barati from comment #4) > how is this spec compliant? Don't we need to run the iterator protocol? Please note we are emitting `spread` & `new_array_with_spread` bytecode ops here, which convert iterable to an array (see `slow_path_spread` and IteratorHelpers.js). The same approach is used in CallFunctionCallDotNode.
Alexey Shvayka
Comment 8 2020-07-22 07:49:41 PDT
Comment on attachment 404922 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=404922&action=review > JSTests/stress/reflect-apply.js:170 > +shouldBe(Reflect.apply(...(function* () { I've added a test case with spread & generator.
Darin Adler
Comment 9 2020-08-14 09:45:32 PDT
Comment on attachment 404922 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=404922&action=review > Source/JavaScriptCore/builtins/BuiltinNames.h:49 > + macro(reflectApplyFunction) \ Originally these were sorted alphabetically, with our traditional WebKit "sorted as the Unix sort tool does". But it seems that people instead started adding new ones at the bottom?
Yusuke Suzuki
Comment 10 2020-08-15 22:27:14 PDT
Comment on attachment 404922 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=404922&action=review Still reviewing, but one comment. > Source/JavaScriptCore/runtime/JSGlobalObject.cpp:1076 > + m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::reflectApplyFunction)].initLater([] (const Initializer<JSCell>& init) { > + init.set(JSFunction::create(init.vm, reflectObjectApplyCodeGenerator(init.vm), init.owner)); > + }); Instead, 1. Let's rename ReflectObject's apply function to `reflectApply` function. 2. Let's mark it as @globalPrivate Then, this is done automatically.
Alexey Shvayka
Comment 11 2020-09-09 12:04:46 PDT
Created attachment 408355 [details] Patch Rename Reflect.apply and mark as @globalPrivate.
Yusuke Suzuki
Comment 12 2020-09-09 12:55:43 PDT
Comment on attachment 408355 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=408355&action=review > Source/JavaScriptCore/runtime/ReflectObject.cpp:82 > + JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().applyPublicName(), reflectObjectReflectApplyCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum)); Let's get link-time-constant from global object instead via `globalObject->linkTimeConstant(...)`.
Alexey Shvayka
Comment 13 2020-09-09 16:25:37 PDT
Created attachment 408382 [details] Patch Avoid creating extra JSFunction for Reflect.apply.
Saam Barati
Comment 14 2020-11-30 16:55:46 PST
Comment on attachment 408382 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=408382&action=review r=me > Source/JavaScriptCore/ChangeLog:17 > + To accomodate monkey-patching, Reflect.apply is exposed via @globalPrivate accomodate => accommodate > Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:2001 > +RegisterID* ReflectApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) I wonder if we can somehow abstract this function to deal with all the variants that are doing similar things.
Note You need to log in before you can comment on or make changes to this bug.