[JSC] __proto__ getter should be fast
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Oct 2017 12:02:01 +0000 (12:02 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Oct 2017 12:02:01 +0000 (12:02 +0000)
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:

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

49 files changed:
JSTests/ChangeLog
JSTests/stress/dfg-object-proto-accessor.js [new file with mode: 0644]
JSTests/stress/dfg-object-proto-getter.js [new file with mode: 0644]
JSTests/stress/dfg-object-prototype-of.js [new file with mode: 0644]
JSTests/stress/dfg-reflect-get-prototype-of.js [new file with mode: 0644]
JSTests/stress/intrinsic-getter-with-poly-proto.js [new file with mode: 0644]
JSTests/stress/object-get-prototype-of-filtered.js [new file with mode: 0644]
JSTests/stress/object-get-prototype-of-mono-proto.js [new file with mode: 0644]
JSTests/stress/object-get-prototype-of-poly-mono-proto.js [new file with mode: 0644]
JSTests/stress/object-get-prototype-of-poly-proto.js [new file with mode: 0644]
JSTests/stress/object-proto-getter-filtered.js [new file with mode: 0644]
JSTests/stress/object-proto-getter-poly-mono-proto.js [new file with mode: 0644]
JSTests/stress/object-proto-getter-poly-proto.js [new file with mode: 0644]
JSTests/stress/object-prototype-proto-accessors-should-throw-on-undefined-this.js
JSTests/stress/string-proto.js [new file with mode: 0644]
LayoutTests/ChangeLog
LayoutTests/js/object-literal-shorthand-construction-expected.txt
LayoutTests/js/script-tests/object-literal-shorthand-construction.js
LayoutTests/js/script-tests/sloppy-getter-setter-global-object.js
LayoutTests/js/sloppy-getter-setter-global-object-expected.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
Source/JavaScriptCore/dfg/DFGHeapLocation.h
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/IntrinsicEmitter.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/runtime/Intrinsic.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
Source/JavaScriptCore/runtime/ObjectConstructor.cpp
Source/JavaScriptCore/runtime/ReflectObject.cpp

index 27c02fc..0f99c6c 100644 (file)
@@ -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"
diff --git a/JSTests/stress/dfg-object-proto-accessor.js b/JSTests/stress/dfg-object-proto-accessor.js
new file mode 100644 (file)
index 0000000..0f6db7b
--- /dev/null
@@ -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);
+    }
+}());
diff --git a/JSTests/stress/dfg-object-proto-getter.js b/JSTests/stress/dfg-object-proto-getter.js
new file mode 100644 (file)
index 0000000..9a0716d
--- /dev/null
@@ -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);
+    }
+}());
diff --git a/JSTests/stress/dfg-object-prototype-of.js b/JSTests/stress/dfg-object-prototype-of.js
new file mode 100644 (file)
index 0000000..4e2684d
--- /dev/null
@@ -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.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e3; ++i)
+        shouldBe(target({}), Object.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e3; ++i) {
+        shouldThrow(() => target(null), `TypeError: null is not an object (evaluating 'Object.getPrototypeOf(object)')`);
+        shouldThrow(() => target(undefined), `TypeError: undefined is not an object (evaluating 'Object.getPrototypeOf(object)')`);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e3; ++i)
+        shouldBe(target("Cocoa"), String.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e3; ++i)
+        shouldBe(target(42), Number.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e3; ++i)
+        shouldBe(target(42.195), Number.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e3; ++i)
+        shouldBe(target(true), Boolean.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e3; ++i)
+        shouldBe(target(Symbol("Cocoa")), Symbol.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(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);
+    }
+}());
diff --git a/JSTests/stress/dfg-reflect-get-prototype-of.js b/JSTests/stress/dfg-reflect-get-prototype-of.js
new file mode 100644 (file)
index 0000000..7c888ff
--- /dev/null
@@ -0,0 +1,58 @@
+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 Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e3; ++i)
+        shouldBe(target({}), Object.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e3; ++i) {
+        shouldThrow(() => target(null), `TypeError: Reflect.getPrototypeOf requires the first argument be an object`);
+        shouldThrow(() => target(undefined), `TypeError: Reflect.getPrototypeOf requires the first argument be an object`);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e3; ++i) {
+        shouldThrow(() => target("Cocoa"), `TypeError: Reflect.getPrototypeOf requires the first argument be an object`);
+        shouldThrow(() => target(42), `TypeError: Reflect.getPrototypeOf requires the first argument be an object`);
+        shouldThrow(() => target(true), `TypeError: Reflect.getPrototypeOf requires the first argument be an object`);
+    }
+}());
diff --git a/JSTests/stress/intrinsic-getter-with-poly-proto.js b/JSTests/stress/intrinsic-getter-with-poly-proto.js
new file mode 100644 (file)
index 0000000..d865235
--- /dev/null
@@ -0,0 +1,31 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function makePolyProtoObject() {
+    function foo() {
+        class C {
+            constructor() {
+                this._field = 42;
+            }
+        };
+        return new C;
+    }
+    for (let i = 0; i < 15; ++i)
+        foo();
+    return foo();
+}
+
+function target(object)
+{
+    return object.__proto__;
+}
+noInline(target);
+
+var polyProtoObject = makePolyProtoObject();
+var prototype = Reflect.getPrototypeOf(polyProtoObject);
+for (var i = 0; i < 1e5; ++i)
+    shouldBe(target(polyProtoObject), prototype);
+polyProtoObject.__proto__ = null;
+shouldBe(target(polyProtoObject), undefined);
diff --git a/JSTests/stress/object-get-prototype-of-filtered.js b/JSTests/stress/object-get-prototype-of-filtered.js
new file mode 100644 (file)
index 0000000..e1a0808
--- /dev/null
@@ -0,0 +1,64 @@
+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)}`);
+}
+
+// In this case, we cannot handle it as GetPrototypeOf since GetById is opaque.
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e4; ++i) {
+        var object = {};
+        object[`Cocoa${i}`] = `Cocoa`;
+        shouldBe(target(object), Object.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e4; ++i) {
+        var array = [];
+        array[`Cocoa${i}`] = `Cocoa`;
+        shouldBe(target(array), Array.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e4; ++i) {
+        function Cocoa() { }
+        Cocoa[`Cocoa${i}`] = `Cocoa`;
+        shouldBe(target(Cocoa), Function.prototype);
+    }
+}());
diff --git a/JSTests/stress/object-get-prototype-of-mono-proto.js b/JSTests/stress/object-get-prototype-of-mono-proto.js
new file mode 100644 (file)
index 0000000..5e0754e
--- /dev/null
@@ -0,0 +1,34 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function makePolyProtoObject() {
+    function foo() {
+        class C {
+            constructor() {
+                this._field = 42;
+                this.hello = 33;
+            }
+        };
+        return new C;
+    }
+    for (let i = 0; i < 15; ++i)
+        foo();
+    return foo();
+}
+
+function target(object)
+{
+    return [object.hello, Object.getPrototypeOf(object)];
+}
+noInline(target);
+
+var polyProtoObject = makePolyProtoObject();
+var prototype = Reflect.getPrototypeOf(polyProtoObject);
+var object1 = { __proto__: prototype, hello: 44 };
+var object2 = { hello: 45 };
+for (var i = 0; i < 1e5; ++i) {
+    shouldBe(target(object1)[1], prototype);
+    shouldBe(target(object2)[1], Object.prototype);
+}
diff --git a/JSTests/stress/object-get-prototype-of-poly-mono-proto.js b/JSTests/stress/object-get-prototype-of-poly-mono-proto.js
new file mode 100644 (file)
index 0000000..3fa09ef
--- /dev/null
@@ -0,0 +1,33 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function makePolyProtoObject() {
+    function foo() {
+        class C {
+            constructor() {
+                this._field = 42;
+                this.hello = 33;
+            }
+        };
+        return new C;
+    }
+    for (let i = 0; i < 15; ++i)
+        foo();
+    return foo();
+}
+
+function target(object)
+{
+    return [object.hello, Object.getPrototypeOf(object)];
+}
+noInline(target);
+
+var polyProtoObject = makePolyProtoObject();
+var prototype = Reflect.getPrototypeOf(polyProtoObject);
+var object = { __proto__: prototype, hello: 44 };
+for (var i = 0; i < 1e5; ++i) {
+    shouldBe(target(polyProtoObject)[1], prototype);
+    shouldBe(target(object)[1], prototype);
+}
diff --git a/JSTests/stress/object-get-prototype-of-poly-proto.js b/JSTests/stress/object-get-prototype-of-poly-proto.js
new file mode 100644 (file)
index 0000000..43d649d
--- /dev/null
@@ -0,0 +1,30 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function makePolyProtoObject() {
+    function foo() {
+        class C {
+            constructor() {
+                this._field = 42;
+                this.hello = 33;
+            }
+        };
+        return new C;
+    }
+    for (let i = 0; i < 15; ++i)
+        foo();
+    return foo();
+}
+
+function target(object)
+{
+    return [object.hello, Object.getPrototypeOf(object)];
+}
+noInline(target);
+
+var polyProtoObject = makePolyProtoObject();
+var prototype = Reflect.getPrototypeOf(polyProtoObject);
+for (var i = 0; i < 1e5; ++i)
+    shouldBe(target(polyProtoObject)[1], prototype);
diff --git a/JSTests/stress/object-proto-getter-filtered.js b/JSTests/stress/object-proto-getter-filtered.js
new file mode 100644 (file)
index 0000000..33fb527
--- /dev/null
@@ -0,0 +1,62 @@
+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 < 1e4; ++i) {
+        var object = {};
+        object[`Cocoa${i}`] = `Cocoa`;
+        shouldBe(target(object), Object.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return object.__proto__;
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e4; ++i) {
+        var array = [];
+        array[`Cocoa${i}`] = `Cocoa`;
+        shouldBe(target(array), Array.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return object.__proto__;
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e4; ++i) {
+        function Cocoa() { }
+        Cocoa[`Cocoa${i}`] = `Cocoa`;
+        shouldBe(target(Cocoa), Function.prototype);
+    }
+}());
diff --git a/JSTests/stress/object-proto-getter-poly-mono-proto.js b/JSTests/stress/object-proto-getter-poly-mono-proto.js
new file mode 100644 (file)
index 0000000..1cc77e5
--- /dev/null
@@ -0,0 +1,34 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function makePolyProtoObject() {
+    function foo() {
+        class C {
+            constructor() {
+                this._field = 42;
+            }
+        };
+        return new C;
+    }
+    for (let i = 0; i < 15; ++i)
+        foo();
+    return foo();
+}
+
+function target(object)
+{
+    return object.__proto__;
+}
+noInline(target);
+
+var polyProtoObject = makePolyProtoObject();
+var prototype = Reflect.getPrototypeOf(polyProtoObject);
+var object = {
+    __proto__: prototype
+};
+for (var i = 0; i < 1e5; ++i) {
+    shouldBe(target(polyProtoObject), prototype);
+    shouldBe(target(object), prototype);
+}
diff --git a/JSTests/stress/object-proto-getter-poly-proto.js b/JSTests/stress/object-proto-getter-poly-proto.js
new file mode 100644 (file)
index 0000000..9b8efd9
--- /dev/null
@@ -0,0 +1,29 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function makePolyProtoObject() {
+    function foo() {
+        class C {
+            constructor() {
+                this._field = 42;
+            }
+        };
+        return new C;
+    }
+    for (let i = 0; i < 15; ++i)
+        foo();
+    return foo();
+}
+
+function target(object)
+{
+    return object.__proto__;
+}
+noInline(target);
+
+var polyProtoObject = makePolyProtoObject();
+var prototype = Reflect.getPrototypeOf(polyProtoObject);
+for (var i = 0; i < 1e5; ++i)
+    shouldBe(target(polyProtoObject), prototype);
index 5ffb4aa..5d4505b 100644 (file)
@@ -34,5 +34,5 @@ function runTest(testId, test, expectedResult, expectedException) {
     }
 }
 
-runTest(10000, testInvokeGetter, undefined, "TypeError: Object.prototype.__proto__ called on null or undefined");
+runTest(10000, testInvokeGetter, undefined, "TypeError: undefined is not an object (evaluating 'getter()')");
 runTest(10100, testInvokeSetter, undefined, "TypeError: Object.prototype.__proto__ called on null or undefined");
diff --git a/JSTests/stress/string-proto.js b/JSTests/stress/string-proto.js
new file mode 100644 (file)
index 0000000..ddcf012
--- /dev/null
@@ -0,0 +1,12 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function target(value)
+{
+    return value.__proto__;
+}
+noInline(target);
+for (var i = 0; i < 1e6; ++i)
+    shouldBe(target("Cocoa"), String.prototype)
index d2dbcc3..44110e9 100644 (file)
@@ -1,3 +1,17 @@
+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.
+
+        * 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:
+
 2017-10-17  Antti Koivisto  <antti@apple.com>
 
         Text nodes with display:contents parent should render as if they were wrapped in an unstyled <span>
index ee16b85..6fb1775 100644 (file)
@@ -64,7 +64,7 @@ PASS !!Object.getOwnPropertyDescriptor({set 42(value){}}, '42').set is true
 PASS this.__proto__ = [] threw exception TypeError: Cannot set prototype of immutable prototype object.
 PASS ({__proto__: this.__proto__}) instanceof Array is false
 PASS __proto__ = [] threw exception TypeError: Object.prototype.__proto__ called on null or undefined.
-PASS ({__proto__: __proto__}) instanceof Array threw exception TypeError: Object.prototype.__proto__ called on null or undefined.
+PASS ({__proto__: __proto__}) instanceof Array threw exception TypeError: undefined is not an object (evaluating '__proto__').
 SyntaxErrors
 PASS ({break}) threw exception SyntaxError: Cannot use the keyword 'break' as a shorthand property name..
 PASS ({case}) threw exception SyntaxError: Cannot use the keyword 'case' as a shorthand property name..
index 0460b66..d604894 100644 (file)
@@ -113,7 +113,7 @@ shouldBeTrue("!!Object.getOwnPropertyDescriptor({set 42(value){}}, '42').set");
 shouldThrow("this.__proto__ = []");
 shouldBeFalse("({__proto__: this.__proto__}) instanceof Array");
 shouldThrow("__proto__ = []", '"TypeError: Object.prototype.__proto__ called on null or undefined"');
-shouldThrow("({__proto__: __proto__}) instanceof Array", '"TypeError: Object.prototype.__proto__ called on null or undefined"');
+shouldThrow("({__proto__: __proto__}) instanceof Array", '"TypeError: undefined is not an object (evaluating \'__proto__\')"');
 
 // Keywords - Syntax Errors
 debug("SyntaxErrors");
index ebcbea9..8803e6b 100644 (file)
@@ -25,12 +25,12 @@ shouldThrow("Object.prototype.valueOf.call(null);");
 shouldNotThrow("Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').get()");
 shouldNotThrow("Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').set(['foo'])");
 
-shouldThrow("(0,Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').get)()", "\"TypeError: Object.prototype.__proto__ called on null or undefined\"");
+shouldThrow("(0,Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').get)()", "\"TypeError: undefined is not an object (evaluating '(0,Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').get)()')\"");
 shouldThrow("(0,Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').set)(['foo'])", "\"TypeError: Object.prototype.__proto__ called on null or undefined\"");
 
 
 var top_level_sloppy_getter = Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').get;
-shouldThrow("top_level_sloppy_getter();", "\"TypeError: Object.prototype.__proto__ called on null or undefined\"");
+shouldThrow("top_level_sloppy_getter();", "\"TypeError: undefined is not an object (evaluating 'top_level_sloppy_getter()')\"");
 
 var top_level_sloppy_setter = Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').set;
 shouldThrow("top_level_sloppy_setter(['foo']);", "\"TypeError: Object.prototype.__proto__ called on null or undefined\"");
index 9339dd3..8faa475 100644 (file)
@@ -8,9 +8,9 @@ PASS Object.prototype.valueOf.call(3); did not throw exception.
 PASS Object.prototype.valueOf.call(null); threw exception TypeError: null is not an object (evaluating 'Object.prototype.valueOf.call(null)').
 PASS Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').get() did not throw exception.
 PASS Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').set(['foo']) did not throw exception.
-PASS (0,Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').get)() threw exception TypeError: Object.prototype.__proto__ called on null or undefined.
+PASS (0,Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').get)() threw exception TypeError: undefined is not an object (evaluating '(0,Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').get)()').
 PASS (0,Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').set)(['foo']) threw exception TypeError: Object.prototype.__proto__ called on null or undefined.
-PASS top_level_sloppy_getter(); threw exception TypeError: Object.prototype.__proto__ called on null or undefined.
+PASS top_level_sloppy_getter(); threw exception TypeError: undefined is not an object (evaluating 'top_level_sloppy_getter()').
 PASS top_level_sloppy_setter(['foo']); threw exception TypeError: Object.prototype.__proto__ called on null or undefined.
 PASS successfullyParsed is true
 
index c92ddc5..719b3c5 100644 (file)
@@ -1,3 +1,92 @@
+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.
+
+        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:
+
 2017-10-17  Keith Miller  <keith_miller@apple.com>
 
         Change WebCore sources to work with unified source builds
index 56e5ac5..cb58bf0 100644 (file)
@@ -2740,6 +2740,48 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         forNode(node).setType(SpecInt32Only);
         break;
     }
+
+    case GetPrototypeOf: {
+        AbstractValue& value = forNode(node->child1());
+        if ((value.m_type && !(value.m_type & ~SpecObject)) && value.m_structure.isFinite()) {
+            bool canFold = !value.m_structure.isClear();
+            JSValue prototype;
+            value.m_structure.forEach([&] (RegisteredStructure structure) {
+                auto getPrototypeMethod = structure->classInfo()->methodTable.getPrototype;
+                MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype;
+                if (getPrototypeMethod != defaultGetPrototype) {
+                    canFold = false;
+                    return;
+                }
+
+                if (structure->hasPolyProto()) {
+                    canFold = false;
+                    return;
+                }
+                if (!prototype)
+                    prototype = structure->storedPrototype();
+                else if (prototype != structure->storedPrototype())
+                    canFold = false;
+            });
+
+            if (prototype && canFold) {
+                setConstant(node, *m_graph.freeze(prototype));
+                break;
+            }
+        }
+
+        switch (node->child1().useKind()) {
+        case ArrayUse:
+        case FunctionUse:
+        case FinalObjectUse:
+            break;
+        default:
+            clobberWorld(node->origin.semantic, clobberLimit);
+            break;
+        }
+        forNode(node).setType(m_graph, SpecObject | SpecOther);
+        break;
+    }
         
     case GetByOffset: {
         StorageAccessData& data = node->storageAccessData();
index 87fd695..7e921ce 100644 (file)
@@ -238,7 +238,7 @@ private:
     template<typename ChecksFunctor>
     bool handleDOMJITCall(Node* callee, int resultOperand, const DOMJIT::Signature*, int registerOffset, int argumentCountIncludingThis, SpeculatedType prediction, const ChecksFunctor& insertChecks);
     template<typename ChecksFunctor>
-    bool handleIntrinsicGetter(int resultOperand, const GetByIdVariant& intrinsicVariant, Node* thisNode, const ChecksFunctor& insertChecks);
+    bool handleIntrinsicGetter(int resultOperand, SpeculatedType prediction, const GetByIdVariant& intrinsicVariant, Node* thisNode, const ChecksFunctor& insertChecks);
     template<typename ChecksFunctor>
     bool handleTypedArrayConstructor(int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, TypedArrayType, const ChecksFunctor& insertChecks);
     template<typename ChecksFunctor>
@@ -2599,6 +2599,24 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         return true;
     }
 
+    case ObjectGetPrototypeOfIntrinsic: {
+        if (argumentCountIncludingThis != 2)
+            return false;
+
+        insertChecks();
+        set(VirtualRegister(resultOperand), addToGraph(GetPrototypeOf, OpInfo(0), OpInfo(prediction), get(virtualRegisterForArgument(1, registerOffset))));
+        return true;
+    }
+
+    case ReflectGetPrototypeOfIntrinsic: {
+        if (argumentCountIncludingThis != 2)
+            return false;
+
+        insertChecks();
+        set(VirtualRegister(resultOperand), addToGraph(GetPrototypeOf, OpInfo(0), OpInfo(prediction), Edge(get(virtualRegisterForArgument(1, registerOffset)), ObjectUse)));
+        return true;
+    }
+
     case IsTypedArrayViewIntrinsic: {
         ASSERT(argumentCountIncludingThis == 2);
 
@@ -2946,7 +2964,7 @@ bool ByteCodeParser::handleDOMJITCall(Node* callTarget, int resultOperand, const
 
 
 template<typename ChecksFunctor>
-bool ByteCodeParser::handleIntrinsicGetter(int resultOperand, const GetByIdVariant& variant, Node* thisNode, const ChecksFunctor& insertChecks)
+bool ByteCodeParser::handleIntrinsicGetter(int resultOperand, SpeculatedType prediction, const GetByIdVariant& variant, Node* thisNode, const ChecksFunctor& insertChecks)
 {
     switch (variant.intrinsic()) {
     case TypedArrayByteLengthIntrinsic: {
@@ -3013,6 +3031,40 @@ bool ByteCodeParser::handleIntrinsicGetter(int resultOperand, const GetByIdVaria
         return true;
     }
 
+    case UnderscoreProtoIntrinsic: {
+        insertChecks();
+
+        bool canFold = !variant.structureSet().isEmpty();
+        JSValue prototype;
+        variant.structureSet().forEach([&] (Structure* structure) {
+            auto getPrototypeMethod = structure->classInfo()->methodTable.getPrototype;
+            MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype;
+            if (getPrototypeMethod != defaultGetPrototype) {
+                canFold = false;
+                return;
+            }
+
+            if (structure->hasPolyProto()) {
+                canFold = false;
+                return;
+            }
+            if (!prototype)
+                prototype = structure->storedPrototype();
+            else if (prototype != structure->storedPrototype())
+                canFold = false;
+        });
+
+        // OK, only one prototype is found. We perform constant folding here.
+        // This information is important for super's constructor call to get new.target constant.
+        if (prototype && canFold) {
+            set(VirtualRegister(resultOperand), weakJSConstant(prototype));
+            return true;
+        }
+
+        set(VirtualRegister(resultOperand), addToGraph(GetPrototypeOf, OpInfo(0), OpInfo(prediction), thisNode));
+        return true;
+    }
+
     default:
         return false;
     }
@@ -3796,7 +3848,7 @@ void ByteCodeParser::handleGetById(
     
     Node* getter = addToGraph(GetGetter, loadedValue);
 
-    if (handleIntrinsicGetter(destinationOperand, variant, base,
+    if (handleIntrinsicGetter(destinationOperand, prediction, variant, base,
             [&] () {
                 addToGraph(CheckCell, OpInfo(m_graph.freeze(variant.intrinsicFunction())), getter);
             })) {
index 68ab243..00641dd 100644 (file)
@@ -1114,6 +1114,23 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         read(MiscFields);
         def(HeapLocation(TypedArrayByteOffsetLoc, MiscFields, node->child1()), LazyNode(node));
         return;
+
+    case GetPrototypeOf: {
+        switch (node->child1().useKind()) {
+        case ArrayUse:
+        case FunctionUse:
+        case FinalObjectUse:
+            read(JSCell_structureID);
+            read(JSObject_butterfly);
+            read(NamedProperties); // Poly proto could load prototype from its slot.
+            def(HeapLocation(PrototypeLoc, NamedProperties, node->child1()), LazyNode(node));
+            return;
+        default:
+            read(World);
+            write(Heap);
+            return;
+        }
+    }
         
     case GetByOffset:
     case GetGetterSetterByOffset: {
index b44d778..b3c623f 100644 (file)
@@ -238,6 +238,7 @@ bool doesGC(Graph& graph, Node* node)
     case StringCharAt:
     case StringCharCodeAt:
     case GetTypedArrayByteOffset:
+    case GetPrototypeOf:
     case PutByValDirect:
     case PutByVal:
     case PutByValAlias:
index 51410bc..7d152ed 100644 (file)
@@ -1603,6 +1603,11 @@ private:
             break;
         }
 
+        case GetPrototypeOf: {
+            fixupGetPrototypeOf(node);
+            break;
+        }
+
         case Phi:
         case Upsilon:
         case EntrySwitch:
@@ -2302,6 +2307,59 @@ private:
         }
     }
 
+    void fixupGetPrototypeOf(Node* node)
+    {
+        // Reflect.getPrototypeOf only accepts Objects. For Reflect.getPrototypeOf, ByteCodeParser attaches ObjectUse edge filter before fixup phase.
+        if (node->child1().useKind() != ObjectUse) {
+            if (node->child1()->shouldSpeculateString()) {
+                insertCheck<StringUse>(node->child1().node());
+                m_graph.convertToConstant(node, m_graph.freeze(m_graph.globalObjectFor(node->origin.semantic)->stringPrototype()));
+                return;
+            }
+            if (node->child1()->shouldSpeculateInt32()) {
+                insertCheck<Int32Use>(node->child1().node());
+                m_graph.convertToConstant(node, m_graph.freeze(m_graph.globalObjectFor(node->origin.semantic)->numberPrototype()));
+                return;
+            }
+            if (enableInt52() && node->child1()->shouldSpeculateAnyInt()) {
+                insertCheck<Int52RepUse>(node->child1().node());
+                m_graph.convertToConstant(node, m_graph.freeze(m_graph.globalObjectFor(node->origin.semantic)->numberPrototype()));
+                return;
+            }
+            if (node->child1()->shouldSpeculateNumber()) {
+                insertCheck<NumberUse>(node->child1().node());
+                m_graph.convertToConstant(node, m_graph.freeze(m_graph.globalObjectFor(node->origin.semantic)->numberPrototype()));
+                return;
+            }
+            if (node->child1()->shouldSpeculateSymbol()) {
+                insertCheck<SymbolUse>(node->child1().node());
+                m_graph.convertToConstant(node, m_graph.freeze(m_graph.globalObjectFor(node->origin.semantic)->symbolPrototype()));
+                return;
+            }
+            if (node->child1()->shouldSpeculateBoolean()) {
+                insertCheck<BooleanUse>(node->child1().node());
+                m_graph.convertToConstant(node, m_graph.freeze(m_graph.globalObjectFor(node->origin.semantic)->booleanPrototype()));
+                return;
+            }
+        }
+
+        if (node->child1()->shouldSpeculateFinalObject()) {
+            fixEdge<FinalObjectUse>(node->child1());
+            node->clearFlags(NodeMustGenerate);
+            return;
+        }
+        if (node->child1()->shouldSpeculateArray()) {
+            fixEdge<ArrayUse>(node->child1());
+            node->clearFlags(NodeMustGenerate);
+            return;
+        }
+        if (node->child1()->shouldSpeculateFunction()) {
+            fixEdge<FunctionUse>(node->child1());
+            node->clearFlags(NodeMustGenerate);
+            return;
+        }
+    }
+
     void fixupToThis(Node* node)
     {
         ECMAMode ecmaMode = m_graph.executableFor(node->origin.semantic)->isStrictMode() ? StrictMode : NotStrictMode;
index 4ed0e47..20d9269 100644 (file)
@@ -151,6 +151,10 @@ void printInternal(PrintStream& out, LocationKind kind)
     case TypedArrayByteOffsetLoc:
         out.print("TypedArrayByteOffsetLoc");
         return;
+
+    case PrototypeLoc:
+        out.print("PrototypeLoc");
+        return;
         
     case StructureLoc:
         out.print("StructureLoc");
index 676565c..a7e1427 100644 (file)
@@ -60,6 +60,7 @@ enum LocationKind {
     SetterLoc,
     StructureLoc,
     TypedArrayByteOffsetLoc,
+    PrototypeLoc,
     StackLoc,
     StackPayloadLoc,
     MapBucketLoc,
index 628bfc7..a692142 100644 (file)
@@ -1548,6 +1548,7 @@ public:
         case GetById:
         case GetByIdFlush:
         case GetByIdWithThis:
+        case GetPrototypeOf:
         case TryGetById:
         case GetByVal:
         case GetByValWithThis:
@@ -2311,6 +2312,11 @@ public:
         return isArraySpeculation(prediction());
     }
 
+    bool shouldSpeculateFunction()
+    {
+        return isFunctionSpeculation(prediction());
+    }
+
     bool shouldSpeculateProxyObject()
     {
         return isProxyObjectSpeculation(prediction());
index 354ad37..0d63e9f 100644 (file)
@@ -251,6 +251,7 @@ namespace JSC { namespace DFG {
     macro(CheckTypeInfoFlags, NodeMustGenerate) /* Takes an OpInfo with the flags you want to test are set */\
     macro(CheckSubClass, NodeMustGenerate) \
     macro(ParseInt, NodeMustGenerate | NodeResultJS) \
+    macro(GetPrototypeOf, NodeMustGenerate | NodeResultJS) \
     \
     /* Atomics object functions. */\
     macro(AtomicsAdd, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
index 0217ea8..01f4213 100644 (file)
@@ -52,6 +52,7 @@
 #include "JSCInlines.h"
 #include "JSFixedArray.h"
 #include "JSGenericTypedArrayViewConstructorInlines.h"
+#include "JSGlobalObjectFunctions.h"
 #include "JSLexicalEnvironment.h"
 #include "JSMap.h"
 #include "JSSet.h"
@@ -2518,6 +2519,36 @@ JSCell* JIT_OPERATION operationJSSetFindBucket(ExecState* exec, JSCell* map, Enc
     return *bucket;
 }
 
+EncodedJSValue JIT_OPERATION operationGetPrototypeOfObject(ExecState* exec, JSObject* thisObject)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    return JSValue::encode(thisObject->getPrototype(vm, exec));
+}
+
+EncodedJSValue JIT_OPERATION operationGetPrototypeOf(ExecState* exec, EncodedJSValue encodedValue)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue thisValue = JSValue::decode(encodedValue).toThis(exec, StrictMode);
+    if (thisValue.isUndefinedOrNull())
+        return throwVMError(exec, scope, createNotAnObjectError(exec, thisValue));
+
+    JSObject* thisObject = jsDynamicCast<JSObject*>(vm, thisValue);
+    if (!thisObject) {
+        JSObject* prototype = thisValue.synthesizePrototype(exec);
+        EXCEPTION_ASSERT(!!scope.exception() == !prototype);
+        if (UNLIKELY(!prototype))
+            return JSValue::encode(JSValue());
+        return JSValue::encode(prototype);
+    }
+
+    scope.release();
+    return JSValue::encode(thisObject->getPrototype(vm, exec));
+}
+
 void JIT_OPERATION operationThrowDFG(ExecState* exec, EncodedJSValue valueToThrow)
 {
     VM& vm = exec->vm();
index b165a9b..702599b 100644 (file)
@@ -77,6 +77,8 @@ EncodedJSValue JIT_OPERATION operationToPrimitive(ExecState*, EncodedJSValue) WT
 EncodedJSValue JIT_OPERATION operationToNumber(ExecState*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByIdWithThis(ExecState*, EncodedJSValue, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValWithThis(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationGetPrototypeOf(ExecState*, EncodedJSValue) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationGetPrototypeOfObject(ExecState*, JSObject*) WTF_INTERNAL;
 char* JIT_OPERATION operationNewArray(ExecState*, Structure*, void*, size_t) WTF_INTERNAL;
 char* JIT_OPERATION operationNewArrayBuffer(ExecState*, Structure*, size_t, size_t) WTF_INTERNAL;
 char* JIT_OPERATION operationNewEmptyArray(ExecState*, Structure*) WTF_INTERNAL;
index d64154f..6220de1 100644 (file)
@@ -723,7 +723,8 @@ private:
         case GetArgument:
         case CallDOMGetter:
         case GetDynamicVar:
-        case WeakMapGet: {
+        case WeakMapGet:
+        case GetPrototypeOf: {
             setPrediction(m_currentNode->getHeapPrediction());
             break;
         }
index 5c71d62..c04b702 100644 (file)
@@ -401,6 +401,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case GetMyArgumentByValOutOfBounds:
     case ForwardVarargs:
     case CreateRest:
+    case GetPrototypeOf:
     case StringReplace:
     case StringReplaceRegExp:
     case GetRegExpObjectLastIndex:
index a6188d4..f46864c 100644 (file)
@@ -9186,13 +9186,23 @@ void SpeculativeJIT::speculateObject(Edge edge)
     speculateObject(edge, operand.gpr());
 }
 
+void SpeculativeJIT::speculateFunction(Edge edge, GPRReg cell)
+{
+    speculateCellType(edge, cell, SpecFunction, JSFunctionType);
+}
+
 void SpeculativeJIT::speculateFunction(Edge edge)
 {
     if (!needsTypeCheck(edge, SpecFunction))
         return;
     
     SpeculateCellOperand operand(this, edge);
-    speculateCellType(edge, operand.gpr(), SpecFunction, JSFunctionType);
+    speculateFunction(edge, operand.gpr());
+}
+
+void SpeculativeJIT::speculateFinalObject(Edge edge, GPRReg cell)
+{
+    speculateCellType(edge, cell, SpecFinalObject, FinalObjectType);
 }
 
 void SpeculativeJIT::speculateFinalObject(Edge edge)
@@ -9201,7 +9211,7 @@ void SpeculativeJIT::speculateFinalObject(Edge edge)
         return;
     
     SpeculateCellOperand operand(this, edge);
-    speculateCellType(edge, operand.gpr(), SpecFinalObject, FinalObjectType);
+    speculateFinalObject(edge, operand.gpr());
 }
 
 void SpeculativeJIT::speculateRegExpObject(Edge edge, GPRReg cell)
@@ -10767,6 +10777,125 @@ void SpeculativeJIT::compileWeakMapGet(Node* node)
     jsValueResult(resultRegs, node);
 }
 
+void SpeculativeJIT::compileGetPrototypeOf(Node* node)
+{
+    switch (node->child1().useKind()) {
+    case ArrayUse:
+    case FunctionUse:
+    case FinalObjectUse: {
+        SpeculateCellOperand object(this, node->child1());
+        GPRTemporary temp(this);
+        GPRTemporary temp2(this);
+
+        GPRReg objectGPR = object.gpr();
+        GPRReg tempGPR = temp.gpr();
+        GPRReg temp2GPR = temp2.gpr();
+
+        switch (node->child1().useKind()) {
+        case ArrayUse:
+            speculateArray(node->child1(), objectGPR);
+            break;
+        case FunctionUse:
+            speculateFunction(node->child1(), objectGPR);
+            break;
+        case FinalObjectUse:
+            speculateFinalObject(node->child1(), objectGPR);
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+            break;
+        }
+
+        m_jit.emitLoadStructure(*m_jit.vm(), objectGPR, tempGPR, temp2GPR);
+
+        AbstractValue& value = m_state.forNode(node->child1());
+        if ((value.m_type && !(value.m_type & ~SpecObject)) && value.m_structure.isFinite()) {
+            bool hasPolyProto = false;
+            bool hasMonoProto = false;
+            value.m_structure.forEach([&] (RegisteredStructure structure) {
+                if (structure->hasPolyProto())
+                    hasPolyProto = true;
+                else
+                    hasMonoProto = true;
+            });
+
+            if (hasMonoProto && !hasPolyProto) {
+#if USE(JSVALUE64)
+                m_jit.load64(MacroAssembler::Address(tempGPR, Structure::prototypeOffset()), tempGPR);
+                jsValueResult(tempGPR, node);
+#else
+                m_jit.load32(MacroAssembler::Address(tempGPR, Structure::prototypeOffset() + TagOffset), temp2GPR);
+                m_jit.load32(MacroAssembler::Address(tempGPR, Structure::prototypeOffset() + PayloadOffset), tempGPR);
+                jsValueResult(temp2GPR, tempGPR, node);
+#endif
+                return;
+            }
+
+            if (hasPolyProto && !hasMonoProto) {
+#if USE(JSVALUE64)
+                m_jit.load64(MacroAssembler::Address(tempGPR, Structure::prototypeOffset()), tempGPR);
+                m_jit.zeroExtend32ToPtr(tempGPR, tempGPR);
+                m_jit.load64(JITCompiler::BaseIndex(objectGPR, tempGPR, JITCompiler::TimesEight, JSObject::offsetOfInlineStorage()), tempGPR);
+                jsValueResult(tempGPR, node);
+#else
+                m_jit.load32(MacroAssembler::Address(tempGPR, Structure::prototypeOffset() + PayloadOffset), tempGPR);
+                m_jit.load32(JITCompiler::BaseIndex(objectGPR, tempGPR, JITCompiler::TimesEight, JSObject::offsetOfInlineStorage() + TagOffset), temp2GPR);
+                m_jit.load32(JITCompiler::BaseIndex(objectGPR, tempGPR, JITCompiler::TimesEight, JSObject::offsetOfInlineStorage() + PayloadOffset), tempGPR);
+                jsValueResult(temp2GPR, tempGPR, node);
+#endif
+                return;
+            }
+        }
+
+#if USE(JSVALUE64)
+        m_jit.load64(MacroAssembler::Address(tempGPR, Structure::prototypeOffset()), tempGPR);
+        auto isMonoProto = m_jit.branchIfNotInt32(JSValueRegs(tempGPR));
+        m_jit.zeroExtend32ToPtr(tempGPR, tempGPR);
+        m_jit.load64(JITCompiler::BaseIndex(objectGPR, tempGPR, JITCompiler::TimesEight, JSObject::offsetOfInlineStorage()), tempGPR);
+        isMonoProto.link(&m_jit);
+        jsValueResult(tempGPR, node);
+#else
+        m_jit.load32(MacroAssembler::Address(tempGPR, Structure::prototypeOffset() + TagOffset), temp2GPR);
+        m_jit.load32(MacroAssembler::Address(tempGPR, Structure::prototypeOffset() + PayloadOffset), tempGPR);
+        auto isMonoProto = m_jit.branch32(CCallHelpers::NotEqual, temp2GPR, TrustedImm32(JSValue::Int32Tag));
+        m_jit.load32(JITCompiler::BaseIndex(objectGPR, tempGPR, JITCompiler::TimesEight, JSObject::offsetOfInlineStorage() + TagOffset), temp2GPR);
+        m_jit.load32(JITCompiler::BaseIndex(objectGPR, tempGPR, JITCompiler::TimesEight, JSObject::offsetOfInlineStorage() + PayloadOffset), tempGPR);
+        isMonoProto.link(&m_jit);
+        jsValueResult(temp2GPR, tempGPR, node);
+#endif
+        return;
+    }
+    case ObjectUse: {
+        SpeculateCellOperand value(this, node->child1());
+        JSValueRegsTemporary result(this);
+
+        GPRReg valueGPR = value.gpr();
+        JSValueRegs resultRegs = result.regs();
+
+        speculateObject(node->child1(), valueGPR);
+
+        flushRegisters();
+        callOperation(operationGetPrototypeOfObject, resultRegs, valueGPR);
+        m_jit.exceptionCheck();
+        jsValueResult(resultRegs, node);
+        return;
+    }
+    default: {
+        JSValueOperand value(this, node->child1());
+        JSValueRegsTemporary result(this);
+
+        JSValueRegs valueRegs = value.jsValueRegs();
+        JSValueRegs resultRegs = result.regs();
+
+        flushRegisters();
+        callOperation(operationGetPrototypeOf, resultRegs, valueRegs);
+        m_jit.exceptionCheck();
+        jsValueResult(resultRegs, node);
+        return;
+    }
+    }
+}
+
 } } // namespace JSC::DFG
 
 #endif
index 414d6a8..b0e03b2 100644 (file)
@@ -987,6 +987,11 @@ public:
         m_jit.setupArgumentsWithExecState(object, TrustedImmPtr(static_cast<size_t>(size)));
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(J_JITOperation_EO operation, JSValueRegs result, GPRReg object)
+    {
+        m_jit.setupArgumentsWithExecState(object);
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(P_JITOperation_EPS operation, GPRReg result, GPRReg old, size_t size)
     {
         m_jit.setupArgumentsWithExecState(old, TrustedImmPtr(size));
@@ -2835,6 +2840,7 @@ public:
     void compileWeakMapGet(Node*);
     void compileLoadKeyFromMapBucket(Node*);
     void compileLoadValueFromMapBucket(Node*);
+    void compileGetPrototypeOf(Node*);
     
 #if USE(JSVALUE32_64)
     template<typename BaseOperandType, typename PropertyOperandType, typename ValueOperandType, typename TagType>
@@ -3084,7 +3090,9 @@ public:
     void speculateObject(Edge);
     void speculateArray(Edge, GPRReg cell);
     void speculateArray(Edge);
+    void speculateFunction(Edge, GPRReg cell);
     void speculateFunction(Edge);
+    void speculateFinalObject(Edge, GPRReg cell);
     void speculateFinalObject(Edge);
     void speculateRegExpObject(Edge, GPRReg cell);
     void speculateRegExpObject(Edge);
index a23b8a1..240a572 100644 (file)
@@ -4434,6 +4434,11 @@ void SpeculativeJIT::compile(Node* node)
         compileGetTypedArrayByteOffset(node);
         break;
     }
+
+    case GetPrototypeOf: {
+        compileGetPrototypeOf(node);
+        break;
+    }
         
     case GetByOffset: {
         StorageOperand storage(this, node->child1());
index 84da999..e4fa86a 100644 (file)
@@ -4650,6 +4650,11 @@ void SpeculativeJIT::compile(Node* node)
         compileGetTypedArrayByteOffset(node);
         break;
     }
+
+    case GetPrototypeOf: {
+        compileGetPrototypeOf(node);
+        break;
+    }
         
     case GetByOffset:
     case GetGetterSetterByOffset: {
index 6926e1e..7e609cc 100644 (file)
@@ -144,6 +144,7 @@ inline CapabilityLevel canCompile(Node* node)
     case ReallocatePropertyStorage:
     case NukeStructureAndSetButterfly:
     case GetTypedArrayByteOffset:
+    case GetPrototypeOf:
     case NotifyWrite:
     case StoreBarrier:
     case FencedStoreBarrier:
index 3ba45af..1732f88 100644 (file)
@@ -821,6 +821,9 @@ private:
         case GetTypedArrayByteOffset:
             compileGetTypedArrayByteOffset();
             break;
+        case GetPrototypeOf:
+            compileGetPrototypeOf();
+            break;
         case AllocatePropertyStorage:
             compileAllocatePropertyStorage();
             break;
@@ -3429,6 +3432,82 @@ private:
 
         setInt32(m_out.castToInt32(m_out.phi(pointerType(), simpleOut, wastefulOut)));
     }
+
+    void compileGetPrototypeOf()
+    {
+        switch (m_node->child1().useKind()) {
+        case ArrayUse:
+        case FunctionUse:
+        case FinalObjectUse: {
+            LValue object = lowCell(m_node->child1());
+            switch (m_node->child1().useKind()) {
+            case ArrayUse:
+                speculateArray(m_node->child1(), object);
+                break;
+            case FunctionUse:
+                speculateFunction(m_node->child1(), object);
+                break;
+            case FinalObjectUse:
+                speculateFinalObject(m_node->child1(), object);
+                break;
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+                break;
+            }
+
+            LValue structure = loadStructure(object);
+
+            AbstractValue& value = m_state.forNode(m_node->child1());
+            if ((value.m_type && !(value.m_type & ~SpecObject)) && value.m_structure.isFinite()) {
+                bool hasPolyProto = false;
+                bool hasMonoProto = false;
+                value.m_structure.forEach([&] (RegisteredStructure structure) {
+                    if (structure->hasPolyProto())
+                        hasPolyProto = true;
+                    else
+                        hasMonoProto = true;
+                });
+
+                if (hasMonoProto && !hasPolyProto) {
+                    setJSValue(m_out.load64(structure, m_heaps.Structure_prototype));
+                    return;
+                }
+
+                if (hasPolyProto && !hasMonoProto) {
+                    LValue prototypeBits = m_out.load64(structure, m_heaps.Structure_prototype);
+                    LValue index = m_out.bitAnd(prototypeBits, m_out.constInt64(UINT_MAX));
+                    setJSValue(m_out.load64(m_out.baseIndex(m_heaps.properties.atAnyNumber(), object, index, ScaleEight, JSObject::offsetOfInlineStorage())));
+                    return;
+                }
+            }
+
+            LBasicBlock continuation = m_out.newBlock();
+            LBasicBlock loadPolyProto = m_out.newBlock();
+
+            LValue prototypeBits = m_out.load64(structure, m_heaps.Structure_prototype);
+            ValueFromBlock directPrototype = m_out.anchor(prototypeBits);
+            m_out.branch(isInt32(prototypeBits), unsure(loadPolyProto), unsure(continuation));
+
+            LBasicBlock lastNext = m_out.appendTo(loadPolyProto, continuation);
+            LValue index = m_out.bitAnd(prototypeBits, m_out.constInt64(UINT_MAX));
+            ValueFromBlock polyProto = m_out.anchor(
+                m_out.load64(m_out.baseIndex(m_heaps.properties.atAnyNumber(), object, index, ScaleEight, JSObject::offsetOfInlineStorage())));
+            m_out.jump(continuation);
+
+            m_out.appendTo(continuation, lastNext);
+            setJSValue(m_out.phi(Int64, directPrototype, polyProto));
+            return;
+        }
+        case ObjectUse: {
+            setJSValue(vmCall(Int64, m_out.operation(operationGetPrototypeOfObject), m_callFrame, lowObject(m_node->child1())));
+            return;
+        }
+        default: {
+            setJSValue(vmCall(Int64, m_out.operation(operationGetPrototypeOf), m_callFrame, lowJSValue(m_node->child1())));
+            return;
+        }
+        }
+    }
     
     void compileGetArrayLength()
     {
@@ -9003,7 +9082,7 @@ private:
         LValue structure = loadStructure(value);
 
         LValue prototypeBits = m_out.load64(structure, m_heaps.Structure_prototype);
-        ValueFromBlock directProtoype = m_out.anchor(prototypeBits);
+        ValueFromBlock directPrototype = m_out.anchor(prototypeBits);
         m_out.branch(isInt32(prototypeBits), unsure(loadPolyProto), unsure(comparePrototype));
 
         m_out.appendTo(loadPolyProto, comparePrototype);
@@ -9013,7 +9092,7 @@ private:
         m_out.jump(comparePrototype);
 
         m_out.appendTo(comparePrototype, notYetInstance);
-        LValue currentPrototype = m_out.phi(Int64, directProtoype, polyProto);
+        LValue currentPrototype = m_out.phi(Int64, directPrototype, polyProto);
         ValueFromBlock isInstanceResult = m_out.anchor(m_out.booleanTrue);
         m_out.branch(
             m_out.equal(currentPrototype, prototype),
index 4fb7a59..6f9909f 100644 (file)
@@ -63,6 +63,11 @@ bool IntrinsicGetterAccessCase::canEmitIntrinsicGetter(JSFunction* getter, Struc
         
         return true;
     }
+    case UnderscoreProtoIntrinsic: {
+        auto getPrototypeMethod = structure->classInfo()->methodTable.getPrototype;
+        MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype;
+        return getPrototypeMethod == defaultGetPrototype;
+    }
     default:
         return false;
     }
@@ -125,6 +130,19 @@ void IntrinsicGetterAccessCase::emitIntrinsicGetter(AccessGenerationState& state
         return;
     }
 
+    case UnderscoreProtoIntrinsic: {
+        if (structure()->hasMonoProto()) {
+            jit.moveValue(structure()->storedPrototype(), valueRegs);
+            state.succeed();
+            return;
+        }
+        // FIXME: Support poly proto for intrinsic getters.
+        // https://bugs.webkit.org/show_bug.cgi?id=177318
+        jit.loadValue(MacroAssembler::Address(baseGPR, sizeof(EncodedJSValue) * structure()->polyProtoOffset() + JSObject::offsetOfInlineStorage()), valueRegs);
+        state.succeed();
+        return;
+    }
+
     default:
         break;
     }
index c0ac2ef..b04be15 100644 (file)
@@ -176,6 +176,7 @@ typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EZ)(ExecState*, int32_t);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EZIcfZ)(ExecState*, int32_t, InlineCallFrame*, int32_t);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EZZ)(ExecState*, int32_t, int32_t);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EZSymtabJ)(ExecState*, int32_t, SymbolTable*, EncodedJSValue);
+typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EO)(ExecState*, JSObject*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EOIUi)(ExecState*, JSObject*, UniquedStringImpl*, uint32_t);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJJI)(ExecState*, EncodedJSValue, EncodedJSValue, UniquedStringImpl*);
 typedef JSCell* (JIT_OPERATION *C_JITOperation_EPUi)(ExecState*, void*, uint32_t);
index 9d12fff..9b5bd08 100644 (file)
@@ -111,6 +111,10 @@ const char* intrinsicName(Intrinsic intrinsic)
         return "RegExpTestIntrinsic";
     case RegExpTestFastIntrinsic:
         return "RegExpTestFastIntrinsic";
+    case ObjectGetPrototypeOfIntrinsic:
+        return "ObjectGetPrototypeOfIntrinsic";
+    case ReflectGetPrototypeOfIntrinsic:
+        return "ReflectGetPrototypeOfIntrinsic";
     case StringPrototypeValueOfIntrinsic:
         return "StringPrototypeValueOfIntrinsic";
     case StringPrototypeReplaceIntrinsic:
@@ -189,6 +193,8 @@ const char* intrinsicName(Intrinsic intrinsic)
         return "TypedArrayByteLengthIntrinsic";
     case TypedArrayByteOffsetIntrinsic:
         return "TypedArrayByteOffsetIntrinsic";
+    case UnderscoreProtoIntrinsic:
+        return "UnderscoreProtoIntrinsic";
     case DFGTrueIntrinsic:
         return "DFGTrueIntrinsic";
     case OSRExitIntrinsic:
index 29d95f7..06cb28a 100644 (file)
@@ -68,6 +68,8 @@ enum Intrinsic {
     RegExpExecIntrinsic,
     RegExpTestIntrinsic,
     RegExpTestFastIntrinsic,
+    ObjectGetPrototypeOfIntrinsic,
+    ReflectGetPrototypeOfIntrinsic,
     StringPrototypeValueOfIntrinsic,
     StringPrototypeReplaceIntrinsic,
     StringPrototypeReplaceRegExpIntrinsic,
@@ -109,6 +111,7 @@ enum Intrinsic {
     TypedArrayLengthIntrinsic,
     TypedArrayByteLengthIntrinsic,
     TypedArrayByteOffsetIntrinsic,
+    UnderscoreProtoIntrinsic,
 
     // Debugging intrinsics. These are meant to be used as testing hacks within
     // jsc.cpp and should never be exposed to users.
index 41335a7..8b08ec6 100644 (file)
@@ -449,7 +449,7 @@ void JSGlobalObject::init(VM& vm)
     m_nullSetterFunction.set(vm, this, NullSetterFunction::create(vm, NullSetterFunction::createStructure(vm, this, m_functionPrototype.get())));
     m_objectPrototype.set(vm, this, ObjectPrototype::create(vm, this, ObjectPrototype::createStructure(vm, this, jsNull())));
     GetterSetter* protoAccessor = GetterSetter::create(vm, this);
-    protoAccessor->setGetter(vm, this, JSFunction::create(vm, this, 0, makeString("get ", vm.propertyNames->underscoreProto.string()), globalFuncProtoGetter));
+    protoAccessor->setGetter(vm, this, JSFunction::create(vm, this, 0, makeString("get ", vm.propertyNames->underscoreProto.string()), globalFuncProtoGetter, UnderscoreProtoIntrinsic));
     protoAccessor->setSetter(vm, this, JSFunction::create(vm, this, 0, makeString("set ", vm.propertyNames->underscoreProto.string()), globalFuncProtoSetter));
     m_objectPrototype->putDirectNonIndexAccessor(vm, vm.propertyNames->underscoreProto, protoAccessor, PropertyAttribute::Accessor | PropertyAttribute::DontEnum);
     m_functionPrototype->structure()->setPrototypeWithoutTransition(vm, m_objectPrototype.get());
index 42209d6..cd686e8 100644 (file)
@@ -64,7 +64,7 @@ using namespace Unicode;
 
 namespace JSC {
 
-static const char* const ObjectProtoCalledOnNullOrUndefinedError = "Object.prototype.__proto__ called on null or undefined";
+const char* const ObjectProtoCalledOnNullOrUndefinedError = "Object.prototype.__proto__ called on null or undefined";
 
 template<unsigned charactersCount>
 static Bitmap<256> makeCharacterBitmap(const char (&characters)[charactersCount])
@@ -706,11 +706,11 @@ EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState* exec)
 
     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
     if (thisValue.isUndefinedOrNull())
-        return throwVMTypeError(exec, scope, ASCIILiteral(ObjectProtoCalledOnNullOrUndefinedError));
+        return throwVMError(exec, scope, createNotAnObjectError(exec, thisValue));
 
     JSObject* thisObject = jsDynamicCast<JSObject*>(vm, thisValue);
     if (!thisObject) {
-        JSObject* prototype = exec->thisValue().synthesizePrototype(exec);
+        JSObject* prototype = thisValue.synthesizePrototype(exec);
         EXCEPTION_ASSERT(!!scope.exception() == !prototype);
         if (UNLIKELY(!prototype))
             return JSValue::encode(JSValue());
index 4e85eda..b6799e2 100644 (file)
@@ -35,6 +35,8 @@ class JSObject;
 // FIXME: These functions should really be in JSGlobalObject.cpp, but putting them there
 // is a 0.5% reduction.
 
+extern const char* const ObjectProtoCalledOnNullOrUndefinedError;
+
 EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState*);
index 228727b..f670bd3 100644 (file)
@@ -67,7 +67,7 @@ const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_i
 
 /* Source for ObjectConstructor.lut.h
 @begin objectConstructorTable
-  getPrototypeOf            objectConstructorGetPrototypeOf             DontEnum|Function 1
+  getPrototypeOf            objectConstructorGetPrototypeOf             DontEnum|Function 1 ObjectGetPrototypeOfIntrinsic
   setPrototypeOf            objectConstructorSetPrototypeOf             DontEnum|Function 2
   getOwnPropertyDescriptor  objectConstructorGetOwnPropertyDescriptor   DontEnum|Function 2
   getOwnPropertyDescriptors objectConstructorGetOwnPropertyDescriptors  DontEnum|Function 1
index 40e19f3..dd0e18e 100644 (file)
@@ -63,7 +63,7 @@ const ClassInfo ReflectObject::s_info = { "Object", &Base::s_info, &reflectObjec
     deleteProperty           JSBuiltin                             DontEnum|Function 2
     get                      reflectObjectGet                      DontEnum|Function 2
     getOwnPropertyDescriptor reflectObjectGetOwnPropertyDescriptor DontEnum|Function 2
-    getPrototypeOf           reflectObjectGetPrototypeOf           DontEnum|Function 1
+    getPrototypeOf           reflectObjectGetPrototypeOf           DontEnum|Function 1 ReflectGetPrototypeOfIntrinsic
     has                      JSBuiltin                             DontEnum|Function 2
     isExtensible             reflectObjectIsExtensible             DontEnum|Function 1
     ownKeys                  reflectObjectOwnKeys                  DontEnum|Function 1