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/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:
* 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):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* 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/194609@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223523 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Constellation committed Oct 17, 2017
1 parent cde46d2 commit 2f8dd5fbd28cb220cf50989024cfe4771e03f67d
Showing with 1,400 additions and 21 deletions.
  1. +68 −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. +89 −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. +18 −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. +3 −3 Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
  47. +2 −0 Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
  48. +1 −1 Source/JavaScriptCore/runtime/ObjectConstructor.cpp
  49. +1 −1 Source/JavaScriptCore/runtime/ReflectObject.cpp
@@ -1,3 +1,71 @@
2017-10-10 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/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-14 Yusuke Suzuki <utatane.tea@gmail.com>

Reland "Add Above/Below comparisons for UInt32 patterns"
@@ -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 2f8dd5f

Please sign in to comment.