Skip to content
Permalink
Browse files
[JSC] __proto__ getter should be fast
https://bugs.webkit.org/show_bug.cgi?id=178067

Reviewed by Saam Barati.

JSTests:

* stress/dfg-object-proto-accessor.js: Added.
(shouldBe):
(shouldThrow):
(target):
* stress/dfg-object-proto-getter.js: Added.
(shouldBe):
(shouldThrow):
(target):
* stress/dfg-object-prototype-of.js: Added.
(shouldBe):
(shouldThrow):
(target):
* stress/dfg-reflect-get-prototype-of.js: Added.
(shouldBe):
(shouldThrow):
(target):
* stress/intrinsic-getter-with-poly-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-get-prototype-of-filtered.js: Added.
(shouldBe):
(shouldThrow):
(target):
(i.Cocoa):
* stress/object-get-prototype-of-mono-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-get-prototype-of-poly-mono-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-get-prototype-of-poly-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-proto-getter-filtered.js: Added.
(shouldBe):
(shouldThrow):
(target):
(i.Cocoa):
* stress/object-proto-getter-poly-mono-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-proto-getter-poly-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-prototype-proto-accessors-should-throw-on-undefined-this.js:
* stress/string-proto.js: Added.
(shouldBe):
(target):

Source/JavaScriptCore:

In our ES6 class implementation, we access __proto__ field to retrieve super constructor.
Currently, it is handled as an usual getter call to a generic function. And DFG just emits
Call node for this. It is inefficient since typically we know the `prototype` of the given
object when accessing `object.__proto__` since we emit CheckStructure for this `object`.
If Structure has mono proto, we can immediately fold it to constant value. If it is poly proto,
we can still change this to efficient access to poly proto slot.

This patch implements GetPrototypeOf DFG node. This node efficiently accesses to prototype of
the given object. And in AI and ByteCodeParser phase, we attempt to fold it to constant.
ByteCodeParser's folding is a bit important since we have `callee.__proto__` code to get super
constructor. If we can change this to constant, we can reify CallLinkInfo with this constant.
This paves the way to optimizing ArrayConstructor super calls[1], which is particularly important
for ARES-6 ML.

And we also optimize Reflect.getPrototypeOf and Object.getPrototypeOf with this GetPrototypeOf node.

Currently, __proto__ access for poly proto object is not handled well in IC. But we add code handling
poly proto in GetPrototypeOf since Reflect.getPrototypeOf and Object.getPrototypeOf can use it.
Once IC starts handling poly proto & intrinsic getter well, this code will be used for that too.

This patch improves SixSpeed super.es6 by 3.42x.

                         baseline                  patched

super.es6           123.6666+-3.9917     ^     36.1684+-1.0351        ^ definitely 3.4192x faster

[1]: https://bugs.webkit.org/show_bug.cgi?id=178064

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
(JSC::DFG::ByteCodeParser::handleIntrinsicGetter):
(JSC::DFG::ByteCodeParser::handleGetById):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixupGetPrototypeOf):
* dfg/DFGHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGHeapLocation.h:
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::shouldSpeculateFunction):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::speculateFunction):
(JSC::DFG::SpeculativeJIT::speculateFinalObject):
(JSC::DFG::SpeculativeJIT::compileGetPrototypeOf):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileGetPrototypeOf):
(JSC::FTL::DFG::LowerDFGToB3::compileInstanceOf):
* jit/IntrinsicEmitter.cpp:
(JSC::IntrinsicGetterAccessCase::canEmitIntrinsicGetter):
(JSC::IntrinsicGetterAccessCase::emitIntrinsicGetter):
* jit/JITOperations.h:
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::booleanPrototype const):
(JSC::JSGlobalObject::numberPrototype const):
(JSC::JSGlobalObject::booleanObjectStructure const):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::globalFuncProtoGetter):
* runtime/JSGlobalObjectFunctions.h:
* runtime/ObjectConstructor.cpp:
* runtime/ReflectObject.cpp:

LayoutTests:

* js/object-literal-shorthand-construction-expected.txt:
* js/script-tests/object-literal-shorthand-construction.js:
(set 2):
(get 1):
* js/script-tests/sloppy-getter-setter-global-object.js:
* js/sloppy-getter-setter-global-object-expected.txt:

Canonical link: https://commits.webkit.org/194651@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223594 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Constellation committed Oct 18, 2017
1 parent 5f69c51 commit 3142a7c88df06cfa7d30c92fc0681daad5e3566d
Showing with 1,413 additions and 25 deletions.
  1. +74 −0 JSTests/ChangeLog
  2. +115 −0 JSTests/stress/dfg-object-proto-accessor.js
  3. +117 −0 JSTests/stress/dfg-object-proto-getter.js
  4. +115 −0 JSTests/stress/dfg-object-prototype-of.js
  5. +58 −0 JSTests/stress/dfg-reflect-get-prototype-of.js
  6. +31 −0 JSTests/stress/intrinsic-getter-with-poly-proto.js
  7. +64 −0 JSTests/stress/object-get-prototype-of-filtered.js
  8. +34 −0 JSTests/stress/object-get-prototype-of-mono-proto.js
  9. +33 −0 JSTests/stress/object-get-prototype-of-poly-mono-proto.js
  10. +30 −0 JSTests/stress/object-get-prototype-of-poly-proto.js
  11. +62 −0 JSTests/stress/object-proto-getter-filtered.js
  12. +34 −0 JSTests/stress/object-proto-getter-poly-mono-proto.js
  13. +29 −0 JSTests/stress/object-proto-getter-poly-proto.js
  14. +1 −1 JSTests/stress/object-prototype-proto-accessors-should-throw-on-undefined-this.js
  15. +12 −0 JSTests/stress/string-proto.js
  16. +14 −0 LayoutTests/ChangeLog
  17. +1 −1 LayoutTests/js/object-literal-shorthand-construction-expected.txt
  18. +1 −1 LayoutTests/js/script-tests/object-literal-shorthand-construction.js
  19. +2 −2 LayoutTests/js/script-tests/sloppy-getter-setter-global-object.js
  20. +2 −2 LayoutTests/js/sloppy-getter-setter-global-object-expected.txt
  21. +95 −0 Source/JavaScriptCore/ChangeLog
  22. +42 −0 Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
  23. +55 −3 Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
  24. +17 −0 Source/JavaScriptCore/dfg/DFGClobberize.h
  25. +1 −0 Source/JavaScriptCore/dfg/DFGDoesGC.cpp
  26. +58 −0 Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
  27. +4 −0 Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
  28. +1 −0 Source/JavaScriptCore/dfg/DFGHeapLocation.h
  29. +6 −0 Source/JavaScriptCore/dfg/DFGNode.h
  30. +1 −0 Source/JavaScriptCore/dfg/DFGNodeType.h
  31. +31 −0 Source/JavaScriptCore/dfg/DFGOperations.cpp
  32. +2 −0 Source/JavaScriptCore/dfg/DFGOperations.h
  33. +2 −1 Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
  34. +1 −0 Source/JavaScriptCore/dfg/DFGSafeToExecute.h
  35. +131 −2 Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
  36. +8 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
  37. +5 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
  38. +5 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
  39. +1 −0 Source/JavaScriptCore/ftl/FTLCapabilities.cpp
  40. +81 −2 Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
  41. +14 −0 Source/JavaScriptCore/jit/IntrinsicEmitter.cpp
  42. +1 −0 Source/JavaScriptCore/jit/JITOperations.h
  43. +6 −0 Source/JavaScriptCore/runtime/Intrinsic.cpp
  44. +3 −0 Source/JavaScriptCore/runtime/Intrinsic.h
  45. +1 −1 Source/JavaScriptCore/runtime/JSGlobalObject.cpp
  46. +5 −4 Source/JavaScriptCore/runtime/JSGlobalObject.h
  47. +3 −3 Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
  48. +2 −0 Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
  49. +1 −1 Source/JavaScriptCore/runtime/ObjectConstructor.cpp
  50. +1 −1 Source/JavaScriptCore/runtime/ReflectObject.cpp
@@ -1,3 +1,77 @@
2017-10-18 Yusuke Suzuki <utatane.tea@gmail.com>

[JSC] __proto__ getter should be fast
https://bugs.webkit.org/show_bug.cgi?id=178067

Reviewed by Saam Barati.

* stress/dfg-object-proto-accessor.js: Added.
(shouldBe):
(shouldThrow):
(target):
* stress/dfg-object-proto-getter.js: Added.
(shouldBe):
(shouldThrow):
(target):
* stress/dfg-object-prototype-of.js: Added.
(shouldBe):
(shouldThrow):
(target):
* stress/dfg-reflect-get-prototype-of.js: Added.
(shouldBe):
(shouldThrow):
(target):
* stress/intrinsic-getter-with-poly-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-get-prototype-of-filtered.js: Added.
(shouldBe):
(shouldThrow):
(target):
(i.Cocoa):
* stress/object-get-prototype-of-mono-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-get-prototype-of-poly-mono-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-get-prototype-of-poly-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-proto-getter-filtered.js: Added.
(shouldBe):
(shouldThrow):
(target):
(i.Cocoa):
* stress/object-proto-getter-poly-mono-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-proto-getter-poly-proto.js: Added.
(shouldBe):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(target):
* stress/object-prototype-proto-accessors-should-throw-on-undefined-this.js:
* stress/string-proto.js: Added.
(shouldBe):
(target):

2017-10-17 Ryan Haddad <ryanhaddad@apple.com>

Unreviewed, rolling out r223523.
@@ -0,0 +1,115 @@
function shouldBe(actual, expected)
{
if (actual !== expected)
throw new Error('bad value: ' + actual);
}

function shouldThrow(func, errorMessage) {
var errorThrown = false;
var error = null;
try {
func();
} catch (e) {
errorThrown = true;
error = e;
}
if (!errorThrown)
throw new Error('not thrown');
if (String(error) !== errorMessage)
throw new Error(`bad error: ${String(error)}`);
}

(function () {
function target(object)
{
return object.__proto__;
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target({}), Object.prototype);
}());

(function () {
function target(object)
{
return object.__proto__;
}
noInline(target);

for (var i = 0; i < 1e3; ++i) {
shouldThrow(() => target(null), `TypeError: null is not an object (evaluating 'object.__proto__')`);
shouldThrow(() => target(undefined), `TypeError: undefined is not an object (evaluating 'object.__proto__')`);
}
}());

(function () {
function target(object)
{
return object.__proto__;
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target("Cocoa"), String.prototype);
}());

(function () {
function target(object)
{
return object.__proto__;
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target(42), Number.prototype);
}());

(function () {
function target(object)
{
return object.__proto__;
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target(42.195), Number.prototype);
}());

(function () {
function target(object)
{
return object.__proto__;
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target(true), Boolean.prototype);
}());

(function () {
function target(object)
{
return object.__proto__;
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target(Symbol("Cocoa")), Symbol.prototype);
}());

(function () {
function target(object)
{
return object.__proto__;
}
noInline(target);

for (var i = 0; i < 1e3; ++i) {
shouldBe(target("Cocoa"), String.prototype);
shouldBe(target(42), Number.prototype);
shouldBe(target(42.195), Number.prototype);
shouldBe(target(true), Boolean.prototype);
shouldBe(target(Symbol("Cocoa")), Symbol.prototype);
}
}());
@@ -0,0 +1,117 @@
function shouldBe(actual, expected)
{
if (actual !== expected)
throw new Error('bad value: ' + actual);
}

function shouldThrow(func, errorMessage) {
var errorThrown = false;
var error = null;
try {
func();
} catch (e) {
errorThrown = true;
error = e;
}
if (!errorThrown)
throw new Error('not thrown');
if (String(error) !== errorMessage)
throw new Error(`bad error: ${String(error)}`);
}

var protoFunction = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").get;

(function () {
function target(object)
{
return protoFunction.call(object);
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target({}), Object.prototype);
}());

(function () {
function target(object)
{
return protoFunction.call(object);
}
noInline(target);

for (var i = 0; i < 1e3; ++i) {
shouldThrow(() => target(null), `TypeError: null is not an object (evaluating 'protoFunction.call(object)')`);
shouldThrow(() => target(undefined), `TypeError: undefined is not an object (evaluating 'protoFunction.call(object)')`);
}
}());

(function () {
function target(object)
{
return protoFunction.call(object);
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target("Cocoa"), String.prototype);
}());

(function () {
function target(object)
{
return protoFunction.call(object);
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target(42), Number.prototype);
}());

(function () {
function target(object)
{
return protoFunction.call(object);
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target(42.195), Number.prototype);
}());

(function () {
function target(object)
{
return protoFunction.call(object);
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target(true), Boolean.prototype);
}());

(function () {
function target(object)
{
return protoFunction.call(object);
}
noInline(target);

for (var i = 0; i < 1e3; ++i)
shouldBe(target(Symbol("Cocoa")), Symbol.prototype);
}());

(function () {
function target(object)
{
return protoFunction.call(object);
}
noInline(target);

for (var i = 0; i < 1e3; ++i) {
shouldBe(target("Cocoa"), String.prototype);
shouldBe(target(42), Number.prototype);
shouldBe(target(42.195), Number.prototype);
shouldBe(target(true), Boolean.prototype);
shouldBe(target(Symbol("Cocoa")), Symbol.prototype);
}
}());

0 comments on commit 3142a7c

Please sign in to comment.