Add DFG/FTL fast path for GetPrototypeOf based on OverridesGetPrototype flag
authorshvaikalesh@gmail.com <shvaikalesh@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 24 Jun 2020 19:51:49 +0000 (19:51 +0000)
committershvaikalesh@gmail.com <shvaikalesh@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 24 Jun 2020 19:51:49 +0000 (19:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=213191

Reviewed by Yusuke Suzuki.

JSTests:

* microbenchmarks/object-get-prototype-of-object-rare.js: Added.
* stress/dfg-object-prototype-of.js: Removed.
* stress/dfg-reflect-get-prototype-of.js: Removed.
* stress/ftl-object-get-prototype-of.js: Added.
* stress/ftl-reflect-get-prototype-of.js: Added.

Source/JavaScriptCore:

This patch:

1. Introduces `loadInlineOffset` LLInt macro (64-bit only) and utilizes it in
   `get_prototype_of` since we assert that `knownPolyProtoOffset` is an inline offset.

2. Brings baseline JIT fast path to 32-bit builds, progressing `super-getter.js`
   microbenchmark by a factor of 10 (w/o DFG).

3. Adds GetPrototypeOf DFG/FTL fast paths that leverage OverridesGetPrototype type
   info flag, advancing provided rare objects microbenchmark by ~46% (~37% w/o FTL).
   Also, cleans up existing DFG fast path by using AssemblyHelpers::loadValue().

4. Extracts AssemblyHelpers::emitLoadPrototype() and uses it in baseline JIT, DFG, and
   InstanceOfGeneric access case. With this change, `instanceof` access case handles all
   [[GetPrototypeOf]] overrides (before: only Proxy objects), which is more correct, yet
   not observable enough to provide a test case. `instanceof` microbenchmarks are neutral.

* bytecode/AccessCase.cpp:
(JSC::AccessCase::generateWithGuard):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileGetPrototypeOf):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileGetPrototypeOf):
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::emitLoadPrototype):
* jit/AssemblyHelpers.h:
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_get_prototype_of):
* llint/LowLevelInterpreter64.asm:

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

16 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/object-get-prototype-of-object-rare.js [new file with mode: 0644]
JSTests/stress/dfg-object-prototype-of.js [deleted file]
JSTests/stress/dfg-reflect-get-prototype-of.js [deleted file]
JSTests/stress/ftl-object-get-prototype-of.js [new file with mode: 0644]
JSTests/stress/ftl-reflect-get-prototype-of.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/AccessCase.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm

index 774d6e9..2f8d3b3 100644 (file)
@@ -1,3 +1,16 @@
+2020-06-24  Alexey Shvayka  <shvaikalesh@gmail.com>
+
+        Add DFG/FTL fast path for GetPrototypeOf based on OverridesGetPrototype flag
+        https://bugs.webkit.org/show_bug.cgi?id=213191
+
+        Reviewed by Yusuke Suzuki.
+
+        * microbenchmarks/object-get-prototype-of-object-rare.js: Added.
+        * stress/dfg-object-prototype-of.js: Removed.
+        * stress/dfg-reflect-get-prototype-of.js: Removed.
+        * stress/ftl-object-get-prototype-of.js: Added.
+        * stress/ftl-reflect-get-prototype-of.js: Added.
+
 2020-06-24  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] Clobberize misses `write(Heap)` report in some nodes
diff --git a/JSTests/microbenchmarks/object-get-prototype-of-object-rare.js b/JSTests/microbenchmarks/object-get-prototype-of-object-rare.js
new file mode 100644 (file)
index 0000000..5b2cc34
--- /dev/null
@@ -0,0 +1,33 @@
+var regExp = /(?:)/;
+var date = new Date();
+var map = new Map();
+var weakSet = new WeakSet();
+var promise = Promise.resolve(1);
+
+function testObjectUse(object) {
+    return Reflect.getPrototypeOf(object);
+}
+noInline(testObjectUse);
+
+function testDefaultCase(value) {
+    return Object.getPrototypeOf(value);
+}
+noInline(testDefaultCase);
+
+function test() {
+    testObjectUse(regExp);
+    testObjectUse(date);
+    testObjectUse(map);
+    testObjectUse(weakSet);
+    testObjectUse(promise);
+
+    testDefaultCase(regExp);
+    testDefaultCase(date);
+    testDefaultCase(map);
+    testDefaultCase(weakSet);
+    testDefaultCase(promise);
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+    test();
diff --git a/JSTests/stress/dfg-object-prototype-of.js b/JSTests/stress/dfg-object-prototype-of.js
deleted file mode 100644 (file)
index 4e2684d..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-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
deleted file mode 100644 (file)
index 7c888ff..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-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/ftl-object-get-prototype-of.js b/JSTests/stress/ftl-object-get-prototype-of.js
new file mode 100644 (file)
index 0000000..8831450
--- /dev/null
@@ -0,0 +1,245 @@
+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 < 1e5; ++i) {
+        shouldBe(target({}), Object.prototype);
+        shouldBe(target((function() {}).prototype), Object.prototype);
+        shouldBe(target((class {}).prototype), Object.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    var F = function() {};
+    var C = class {};
+
+    for (var i = 0; i < 1e5; ++i) {
+        shouldBe(target(new F()), F.prototype);
+        shouldBe(target(new C()), C.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++i) {
+        shouldBe(target(Array.prototype), Object.prototype);
+        shouldBe(target([]), Array.prototype);
+        shouldBe(target(Array(3)), Array.prototype);
+        shouldBe(target(new Array(1, 2, 3)), Array.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++i) {
+        shouldBe(target(Object.prototype), null);
+        shouldBe(target(Object.create(null)), null);
+        shouldBe(target(Object.setPrototypeOf({}, null)), null);
+        shouldBe(target({__proto__: null}), null);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++i) {
+        shouldBe(target(function() {}), Function.prototype);
+        shouldBe(target(class {}), Function.prototype);
+        shouldBe(target(() => {}), Function.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    var regExp = /(?:)/;
+    var date = new Date();
+    var map = new Map();
+    var weakSet = new WeakSet();
+    var promise = Promise.resolve(1);
+
+    for (var i = 0; i < 1e5; ++i) {
+        shouldBe(target(regExp), RegExp.prototype);
+        shouldBe(target(date), Date.prototype);
+        shouldBe(target(map), Map.prototype);
+        shouldBe(target(weakSet), WeakSet.prototype);
+        shouldBe(target(promise), Promise.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    function makePolyProtoObject() {
+        function foo() {
+            class C {
+                constructor() {
+                    this._field = 42;
+                }
+            }
+            lastProto = C.prototype;
+            return new C();
+        }
+        for (let i = 0; i < 1000; ++i)
+            foo();
+        return foo();
+    }
+
+    var polyProtoObject = makePolyProtoObject();
+
+    for (var i = 0; i < 1e5; ++i)
+        shouldBe(target(polyProtoObject), polyProtoObject.constructor.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    var proxyPrototype = {};
+    var proxy = new Proxy({}, {
+        getPrototypeOf: () => proxyPrototype,
+    });
+
+    for (var i = 0; i < 1e5; ++i)
+        shouldBe(target(proxy), proxyPrototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++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 < 1e5; ++i)
+        shouldBe(target("Cocoa"), String.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++i)
+        shouldBe(target(42), Number.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++i)
+        shouldBe(target(42.195), Number.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++i)
+        shouldBe(target(true), Boolean.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++i)
+        shouldBe(target(Symbol()), Symbol.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Object.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++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()), Symbol.prototype);
+        shouldBe(target(1n), BigInt.prototype);
+    }
+}());
diff --git a/JSTests/stress/ftl-reflect-get-prototype-of.js b/JSTests/stress/ftl-reflect-get-prototype-of.js
new file mode 100644 (file)
index 0000000..ea2dddf
--- /dev/null
@@ -0,0 +1,245 @@
+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 < 1e5; ++i) {
+        shouldBe(target({}), Object.prototype);
+        shouldBe(target((function() {}).prototype), Object.prototype);
+        shouldBe(target((class {}).prototype), Object.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    var F = function() {};
+    var C = class {};
+
+    for (var i = 0; i < 1e5; ++i) {
+        shouldBe(target(new F()), F.prototype);
+        shouldBe(target(new C()), C.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++i) {
+        shouldBe(target(Array.prototype), Object.prototype);
+        shouldBe(target([]), Array.prototype);
+        shouldBe(target(Array(3)), Array.prototype);
+        shouldBe(target(new Array(1, 2, 3)), Array.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++i) {
+        shouldBe(target(Object.prototype), null);
+        shouldBe(target(Object.create(null)), null);
+        shouldBe(target(Object.setPrototypeOf({}, null)), null);
+        shouldBe(target({__proto__: null}), null);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++i) {
+        shouldBe(target(function() {}), Function.prototype);
+        shouldBe(target(class {}), Function.prototype);
+        shouldBe(target(() => {}), Function.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    var regExp = /(?:)/;
+    var date = new Date();
+    var map = new Map();
+    var weakSet = new WeakSet();
+    var promise = Promise.resolve(1);
+
+    for (var i = 0; i < 1e5; ++i) {
+        shouldBe(target(regExp), RegExp.prototype);
+        shouldBe(target(date), Date.prototype);
+        shouldBe(target(map), Map.prototype);
+        shouldBe(target(weakSet), WeakSet.prototype);
+        shouldBe(target(promise), Promise.prototype);
+    }
+}());
+
+(function () {
+    function target(object)
+    {
+        return Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    function makePolyProtoObject() {
+        function foo() {
+            class C {
+                constructor() {
+                    this._field = 42;
+                }
+            }
+            lastProto = C.prototype;
+            return new C();
+        }
+        for (let i = 0; i < 1000; ++i)
+            foo();
+        return foo();
+    }
+
+    var polyProtoObject = makePolyProtoObject();
+
+    for (var i = 0; i < 1e5; ++i)
+        shouldBe(target(polyProtoObject), polyProtoObject.constructor.prototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    var proxyPrototype = {};
+    var proxy = new Proxy({}, {
+        getPrototypeOf: () => proxyPrototype,
+    });
+
+    for (var i = 0; i < 1e5; ++i)
+        shouldBe(target(proxy), proxyPrototype);
+}());
+
+(function () {
+    function target(object)
+    {
+        return Reflect.getPrototypeOf(object);
+    }
+    noInline(target);
+
+    for (var i = 0; i < 1e5; ++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 < 1e5; ++i)
+        shouldThrow(() => target("Cocoa"), `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 < 1e5; ++i)
+        shouldThrow(() => target(42), `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 < 1e5; ++i)
+        shouldThrow(() => target(42.195), `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 < 1e5; ++i)
+        shouldThrow(() => target(true), `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 < 1e5; ++i)
+        shouldThrow(() => target(Symbol()), `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 < 1e5; ++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(42.195), `TypeError: Reflect.getPrototypeOf requires the first argument be an object`);
+        shouldThrow(() => target(true), `TypeError: Reflect.getPrototypeOf requires the first argument be an object`);
+        shouldThrow(() => target(Symbol()), `TypeError: Reflect.getPrototypeOf requires the first argument be an object`);
+        shouldThrow(() => target(1n), `TypeError: Reflect.getPrototypeOf requires the first argument be an object`);
+    }
+}());
index d9e4adc..f1b58a6 100644 (file)
@@ -1,3 +1,44 @@
+2020-06-24  Alexey Shvayka  <shvaikalesh@gmail.com>
+
+        Add DFG/FTL fast path for GetPrototypeOf based on OverridesGetPrototype flag
+        https://bugs.webkit.org/show_bug.cgi?id=213191
+
+        Reviewed by Yusuke Suzuki.
+
+        This patch:
+
+        1. Introduces `loadInlineOffset` LLInt macro (64-bit only) and utilizes it in
+           `get_prototype_of` since we assert that `knownPolyProtoOffset` is an inline offset.
+
+        2. Brings baseline JIT fast path to 32-bit builds, progressing `super-getter.js`
+           microbenchmark by a factor of 10 (w/o DFG).
+
+        3. Adds GetPrototypeOf DFG/FTL fast paths that leverage OverridesGetPrototype type
+           info flag, advancing provided rare objects microbenchmark by ~46% (~37% w/o FTL).
+           Also, cleans up existing DFG fast path by using AssemblyHelpers::loadValue().
+
+        4. Extracts AssemblyHelpers::emitLoadPrototype() and uses it in baseline JIT, DFG, and
+           InstanceOfGeneric access case. With this change, `instanceof` access case handles all
+           [[GetPrototypeOf]] overrides (before: only Proxy objects), which is more correct, yet
+           not observable enough to provide a test case. `instanceof` microbenchmarks are neutral.
+
+        * bytecode/AccessCase.cpp:
+        (JSC::AccessCase::generateWithGuard):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileGetPrototypeOf):
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetPrototypeOf):
+        * jit/AssemblyHelpers.cpp:
+        (JSC::AssemblyHelpers::emitLoadPrototype):
+        * jit/AssemblyHelpers.h:
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        (JSC::JIT::privateCompileSlowCases):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_get_prototype_of):
+        * llint/LowLevelInterpreter64.asm:
+
 2020-06-24  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] Clobberize misses `write(Heap)` report in some nodes
index d8d806d..4a3bb5a 100644 (file)
@@ -1278,36 +1278,19 @@ void AccessCase::generateWithGuard(
             allocator.preserveReusedRegistersByPushing(
                 jit,
                 ScratchRegisterAllocator::ExtraStackSpace::NoExtraSpace);
-        CCallHelpers::Jump failAndIgnore;
+        CCallHelpers::JumpList failAndIgnore;
 
         jit.move(baseGPR, valueGPR);
         
         CCallHelpers::Label loop(&jit);
-        failAndIgnore = jit.branchIfType(valueGPR, ProxyObjectType);
-        
-        jit.emitLoadStructure(vm, valueGPR, scratch2GPR, scratchGPR);
+
 #if USE(JSVALUE64)
-        jit.load64(CCallHelpers::Address(scratch2GPR, Structure::prototypeOffset()), scratch2GPR);
-        CCallHelpers::Jump hasMonoProto = jit.branchTest64(CCallHelpers::NonZero, scratch2GPR);
-        jit.load64(
-            CCallHelpers::Address(valueGPR, offsetRelativeToBase(knownPolyProtoOffset)),
-            scratch2GPR);
-        hasMonoProto.link(&jit);
+        JSValueRegs resultRegs(scratch2GPR);
 #else
-        jit.load32(
-            CCallHelpers::Address(scratch2GPR, Structure::prototypeOffset() + TagOffset),
-            scratchGPR);
-        jit.load32(
-            CCallHelpers::Address(scratch2GPR, Structure::prototypeOffset() + PayloadOffset),
-            scratch2GPR);
-        CCallHelpers::Jump hasMonoProto = jit.branch32(
-            CCallHelpers::NotEqual, scratchGPR, CCallHelpers::TrustedImm32(JSValue::EmptyValueTag));
-        jit.load32(
-            CCallHelpers::Address(
-                valueGPR, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset),
-            scratch2GPR);
-        hasMonoProto.link(&jit);
+        JSValueRegs resultRegs(scratchGPR, scratch2GPR);
 #endif
+
+        jit.emitLoadPrototype(vm, valueGPR, resultRegs, scratchGPR, failAndIgnore);
         jit.move(scratch2GPR, valueGPR);
         
         CCallHelpers::Jump isInstance = jit.branchPtr(CCallHelpers::Equal, valueGPR, prototypeGPR);
index 3a1419e..7ad6a6c 100644 (file)
@@ -13876,17 +13876,24 @@ void SpeculativeJIT::compileWeakMapSet(Node* node)
 
 void SpeculativeJIT::compileGetPrototypeOf(Node* node)
 {
+    GPRTemporary temp(this);
+    GPRTemporary temp2(this);
+
+    GPRReg tempGPR = temp.gpr();
+    GPRReg temp2GPR = temp2.gpr();
+
+#if USE(JSVALUE64)
+    JSValueRegs resultRegs(tempGPR);
+#else
+    JSValueRegs resultRegs(temp2GPR, tempGPR);
+#endif
+
     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:
@@ -13917,74 +13924,52 @@ void SpeculativeJIT::compileGetPrototypeOf(Node* node)
             });
 
             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
+                m_jit.loadValue(MacroAssembler::Address(tempGPR, Structure::prototypeOffset()), resultRegs);
+                jsValueResult(resultRegs, node);
                 return;
             }
 
             if (hasPolyProto && !hasMonoProto) {
-#if USE(JSVALUE64)
-                m_jit.load64(JITCompiler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset)), tempGPR);
-                jsValueResult(tempGPR, node);
-#else
-                m_jit.load32(JITCompiler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset) + TagOffset), temp2GPR);
-                m_jit.load32(JITCompiler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), tempGPR);
-                jsValueResult(temp2GPR, tempGPR, node);
-#endif
+                m_jit.loadValue(JITCompiler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset)), resultRegs);
+                jsValueResult(resultRegs, node);
                 return;
             }
         }
 
-#if USE(JSVALUE64)
-        m_jit.load64(MacroAssembler::Address(tempGPR, Structure::prototypeOffset()), tempGPR);
-        auto hasMonoProto = m_jit.branchIfNotEmpty(tempGPR);
-        m_jit.load64(JITCompiler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset)), tempGPR);
-        hasMonoProto.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 hasMonoProto = m_jit.branchIfNotEmpty(temp2GPR);
-        m_jit.load32(JITCompiler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset) + TagOffset), temp2GPR);
-        m_jit.load32(JITCompiler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), tempGPR);
+        m_jit.loadValue(MacroAssembler::Address(tempGPR, Structure::prototypeOffset()), resultRegs);
+        auto hasMonoProto = m_jit.branchIfNotEmpty(resultRegs);
+        m_jit.loadValue(JITCompiler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset)), resultRegs);
         hasMonoProto.link(&m_jit);
-        jsValueResult(temp2GPR, tempGPR, node);
-#endif
+        jsValueResult(resultRegs, node);
         return;
     }
     case ObjectUse: {
-        // FIXME: Add fast path based on OverridesGetPrototype type info flag
-        // https://bugs.webkit.org/show_bug.cgi?id=213191
-        SpeculateCellOperand value(this, node->child1());
-        JSValueRegsTemporary result(this);
-
-        GPRReg valueGPR = value.gpr();
-        JSValueRegs resultRegs = result.regs();
+        SpeculateCellOperand object(this, node->child1());
+        GPRReg objectGPR = object.gpr();
+        speculateObject(node->child1(), objectGPR);
 
-        speculateObject(node->child1(), valueGPR);
+        JITCompiler::JumpList slowCases;
+        m_jit.emitLoadPrototype(vm(), objectGPR, resultRegs, temp2GPR, slowCases);
+        addSlowPathGenerator(slowPathCall(slowCases, this, operationGetPrototypeOfObject,
+            resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), objectGPR));
 
-        flushRegisters();
-        callOperation(operationGetPrototypeOfObject, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), 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, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueRegs);
-        m_jit.exceptionCheck();
+        JITCompiler::JumpList slowCases;
+        slowCases.append(m_jit.branchIfNotCell(valueRegs));
+
+        GPRReg valueGPR = valueRegs.payloadGPR();
+        slowCases.append(m_jit.branchIfNotObject(valueGPR));
+
+        m_jit.emitLoadPrototype(vm(), valueGPR, resultRegs, temp2GPR, slowCases);
+        addSlowPathGenerator(slowPathCall(slowCases, this, operationGetPrototypeOf,
+            resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), valueRegs));
+
         jsValueResult(resultRegs, node);
         return;
     }
index 2395b61..0d30453 100644 (file)
@@ -142,6 +142,7 @@ namespace JSC { namespace FTL {
     macro(Structure_globalObject, Structure::globalObjectOffset()) \
     macro(Structure_indexingModeIncludingHistory, Structure::indexingModeIncludingHistoryOffset()) \
     macro(Structure_inlineCapacity, Structure::inlineCapacityOffset()) \
+    macro(Structure_outOfLineTypeFlags, Structure::outOfLineTypeFlagsOffset()) \
     macro(Structure_previousOrRareData, Structure::previousOrRareDataOffset()) \
     macro(Structure_prototype, Structure::prototypeOffset()) \
     macro(Structure_structureID, Structure::structureIDOffset()) \
index 39f85e5..dea6cc3 100644 (file)
@@ -4379,11 +4379,20 @@ private:
     void compileGetPrototypeOf()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+
+        LValue object = nullptr;
+        LValue structure = nullptr;
+        ValueFromBlock slowResult;
+
+        LBasicBlock loadPolyProto = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+        LBasicBlock lastNext = m_out.insertNewBlocksBefore(continuation);
+
         switch (m_node->child1().useKind()) {
         case ArrayUse:
         case FunctionUse:
         case FinalObjectUse: {
-            LValue object = lowCell(m_node->child1());
+            object = lowCell(m_node->child1());
             switch (m_node->child1().useKind()) {
             case ArrayUse:
                 speculateArray(m_node->child1(), object);
@@ -4399,7 +4408,7 @@ private:
                 break;
             }
 
-            LValue structure = loadStructure(object);
+            structure = loadStructure(object);
 
             AbstractValue& value = m_state.forNode(m_node->child1());
             if ((value.m_type && !(value.m_type & ~SpecObject)) && value.m_structure.isFinite()) {
@@ -4423,33 +4432,72 @@ private:
                 }
             }
 
-            LBasicBlock continuation = m_out.newBlock();
-            LBasicBlock loadPolyProto = m_out.newBlock();
+            break;
+        }
+        case ObjectUse: {
+            object = lowObject(m_node->child1());
 
-            LValue prototypeBits = m_out.load64(structure, m_heaps.Structure_prototype);
-            ValueFromBlock directPrototype = m_out.anchor(prototypeBits);
-            m_out.branch(m_out.isZero64(prototypeBits), unsure(loadPolyProto), unsure(continuation));
+            LBasicBlock fastPath = m_out.newBlock();
+            LBasicBlock slowPath = m_out.newBlock();
 
-            LBasicBlock lastNext = m_out.appendTo(loadPolyProto, continuation);
-            ValueFromBlock polyProto = m_out.anchor(
-                m_out.load64(m_out.baseIndex(m_heaps.properties.atAnyNumber(), object, m_out.constInt64(knownPolyProtoOffset), ScaleEight, JSObject::offsetOfInlineStorage())));
+            structure = loadStructure(object);
+            m_out.branch(
+                m_out.testIsZero32(
+                    m_out.load16ZeroExt32(structure, m_heaps.Structure_outOfLineTypeFlags),
+                    m_out.constInt32(OverridesGetPrototypeOutOfLine)),
+                usually(fastPath), rarely(slowPath));
+
+            m_out.appendTo(slowPath, fastPath);
+            slowResult = m_out.anchor(vmCall(Int64, operationGetPrototypeOfObject, weakPointer(globalObject), object));
             m_out.jump(continuation);
 
-            m_out.appendTo(continuation, lastNext);
-            setJSValue(m_out.phi(Int64, directPrototype, polyProto));
-            return;
-        }
-        case ObjectUse: {
-            // FIXME: Add fast path based on OverridesGetPrototype type info flag
-            // https://bugs.webkit.org/show_bug.cgi?id=213191
-            setJSValue(vmCall(Int64, operationGetPrototypeOfObject, weakPointer(globalObject), lowObject(m_node->child1())));
-            return;
+            m_out.appendTo(fastPath, loadPolyProto);
+            break;
         }
         default: {
-            setJSValue(vmCall(Int64, operationGetPrototypeOf, weakPointer(globalObject), lowJSValue(m_node->child1())));
-            return;
+            object = lowJSValue(m_node->child1());
+            SpeculatedType valueType = provenType(m_node->child1());
+
+            LBasicBlock isCellPath = m_out.newBlock();
+            LBasicBlock isObjectPath = m_out.newBlock();
+            LBasicBlock fastPath = m_out.newBlock();
+            LBasicBlock slowPath = m_out.newBlock();
+
+            m_out.branch(isCell(object, valueType), usually(isCellPath), rarely(slowPath));
+            m_out.appendTo(isCellPath, isObjectPath);
+            m_out.branch(isObject(object, valueType), usually(isObjectPath), rarely(slowPath));
+
+            m_out.appendTo(isObjectPath, slowPath);
+            structure = loadStructure(object);
+            m_out.branch(
+                m_out.testIsZero32(
+                    m_out.load16ZeroExt32(structure, m_heaps.Structure_outOfLineTypeFlags),
+                    m_out.constInt32(OverridesGetPrototypeOutOfLine)),
+                usually(fastPath), rarely(slowPath));
+
+            m_out.appendTo(slowPath, fastPath);
+            slowResult = m_out.anchor(vmCall(Int64, operationGetPrototypeOf, weakPointer(globalObject), object));
+            m_out.jump(continuation);
+
+            m_out.appendTo(fastPath, loadPolyProto);
+            break;
         }
         }
+
+        ASSERT(object);
+        ASSERT(structure);
+
+        LValue prototypeBits = m_out.load64(structure, m_heaps.Structure_prototype);
+        ValueFromBlock monoProto = m_out.anchor(prototypeBits);
+        m_out.branch(m_out.isZero64(prototypeBits), unsure(loadPolyProto), unsure(continuation));
+
+        m_out.appendTo(loadPolyProto, continuation);
+        ValueFromBlock polyProto = m_out.anchor(
+            m_out.load64(m_out.baseIndex(m_heaps.properties.atAnyNumber(), object, m_out.constInt64(knownPolyProtoOffset), ScaleEight, JSObject::offsetOfInlineStorage())));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setJSValue(m_out.phi(Int64, monoProto, polyProto, slowResult));
     }
     
     void compileGetArrayLength()
index 0cad5e0..114045f 100644 (file)
@@ -392,6 +392,25 @@ void AssemblyHelpers::emitLoadStructure(VM& vm, RegisterID source, RegisterID de
 #endif // not USE(JSVALUE64)
 }
 
+void AssemblyHelpers::emitLoadPrototype(VM& vm, GPRReg objectGPR, JSValueRegs resultRegs, GPRReg scratchGPR, JumpList& slowPath)
+{
+    ASSERT(resultRegs.payloadGPR() != objectGPR);
+    ASSERT(resultRegs.payloadGPR() != scratchGPR);
+    ASSERT(objectGPR != scratchGPR);
+
+    emitLoadStructure(vm, objectGPR, resultRegs.payloadGPR(), scratchGPR);
+
+    auto overridesGetPrototype = branchTest32(MacroAssembler::NonZero,
+        MacroAssembler::Address(resultRegs.payloadGPR(), Structure::outOfLineTypeFlagsOffset()),
+        TrustedImm32(OverridesGetPrototypeOutOfLine));
+    slowPath.append(overridesGetPrototype);
+
+    loadValue(MacroAssembler::Address(resultRegs.payloadGPR(), Structure::prototypeOffset()), resultRegs);
+    auto hasMonoProto = branchIfNotEmpty(resultRegs);
+    loadValue(MacroAssembler::Address(objectGPR, offsetRelativeToBase(knownPolyProtoOffset)), resultRegs);
+    hasMonoProto.link(this);
+}
+
 void AssemblyHelpers::makeSpaceOnStackForCCall()
 {
     unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), maxFrameExtentForSlowPathCall);
index d7128f6..194b7e8 100644 (file)
@@ -1668,6 +1668,7 @@ public:
     }
     
     void emitLoadStructure(VM&, RegisterID source, RegisterID dest, RegisterID scratch);
+    void emitLoadPrototype(VM&, GPRReg objectGPR, JSValueRegs resultRegs, GPRReg scratchGPR, JumpList& slowPath);
 
     void emitStoreStructureWithTypeInfo(TrustedImmPtr structure, RegisterID dest, RegisterID)
     {
index 6005cd4..19496a9 100644 (file)
@@ -318,9 +318,6 @@ void JIT::privateCompileMainPass()
         DEFINE_SLOW_OP(create_async_generator)
         DEFINE_SLOW_OP(new_generator)
         DEFINE_SLOW_OP(pow)
-#if !USE(JSVALUE64)
-        DEFINE_SLOW_OP(get_prototype_of)
-#endif
 
         DEFINE_OP(op_add)
         DEFINE_OP(op_bitnot)
@@ -360,6 +357,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_get_by_id_with_this)
         DEFINE_OP(op_get_by_id_direct)
         DEFINE_OP(op_get_by_val)
+        DEFINE_OP(op_get_prototype_of)
         DEFINE_OP(op_overrides_has_instance)
         DEFINE_OP(op_instanceof)
         DEFINE_OP(op_instanceof_custom)
@@ -474,9 +472,6 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_log_shadow_chicken_prologue)
         DEFINE_OP(op_log_shadow_chicken_tail)
 
-#if USE(JSVALUE64)
-        DEFINE_OP(op_get_prototype_of)
-#endif
         default:
             RELEASE_ASSERT_NOT_REACHED();
         }
@@ -625,15 +620,13 @@ void JIT::privateCompileSlowCases()
         DEFINE_SLOWCASE_SLOW_OP(stricteq)
         DEFINE_SLOWCASE_SLOW_OP(nstricteq)
         DEFINE_SLOWCASE_SLOW_OP(get_direct_pname)
+        DEFINE_SLOWCASE_SLOW_OP(get_prototype_of)
         DEFINE_SLOWCASE_SLOW_OP(has_structure_property)
         DEFINE_SLOWCASE_SLOW_OP(has_own_structure_property)
         DEFINE_SLOWCASE_SLOW_OP(in_structure_property)
         DEFINE_SLOWCASE_SLOW_OP(resolve_scope)
         DEFINE_SLOWCASE_SLOW_OP(check_tdz)
         DEFINE_SLOWCASE_SLOW_OP(to_property_key)
-#if USE(JSVALUE64)
-        DEFINE_SLOWCASE_SLOW_OP(get_prototype_of)
-#endif
         default:
             RELEASE_ASSERT_NOT_REACHED();
         }
index 59f9852..8d03d15 100644 (file)
@@ -1525,26 +1525,6 @@ void JIT::emit_op_get_direct_pname(const Instruction* currentInstruction)
     emitPutVirtualRegister(dst, regT0);
 }
 
-void JIT::emit_op_get_prototype_of(const Instruction* currentInstruction)
-{
-    auto bytecode = currentInstruction->as<OpGetPrototypeOf>();
-    emitGetVirtualRegister(bytecode.m_value, regT0);
-
-    addSlowCase(branchIfNotCell(regT0));
-    addSlowCase(branchIfNotObject(regT0));
-
-    emitLoadStructure(vm(), regT0, regT2, regT1);
-    addSlowCase(branchTest32(NonZero, Address(regT2, Structure::outOfLineTypeFlagsOffset()), TrustedImm32(OverridesGetPrototypeOutOfLine)));
-
-    load64(Address(regT2, Structure::prototypeOffset()), regT2);
-    Jump hasMonoProto = branchTest64(NonZero, regT2);
-    load64(Address(regT0, offsetRelativeToBase(knownPolyProtoOffset)), regT2);
-    hasMonoProto.link(this);
-
-    emitValueProfilingSite(bytecode.metadata(m_codeBlock));
-    emitStoreCell(bytecode.m_dst, regT2);
-}
-
 void JIT::emit_op_enumerator_structure_pname(const Instruction* currentInstruction)
 {
     auto bytecode = currentInstruction->as<OpEnumeratorStructurePname>();
@@ -1765,6 +1745,32 @@ void JIT::emit_op_get_argument(const Instruction* currentInstruction)
     emitPutVirtualRegister(dst, resultRegs);
 }
 
+void JIT::emit_op_get_prototype_of(const Instruction* currentInstruction)
+{
+    auto bytecode = currentInstruction->as<OpGetPrototypeOf>();
+#if USE(JSVALUE64)
+    JSValueRegs valueRegs(regT0);
+    JSValueRegs resultRegs(regT2);
+    GPRReg scratchGPR = regT3;
+#else
+    JSValueRegs valueRegs(regT1, regT0);
+    JSValueRegs resultRegs(regT3, regT2);
+    GPRReg scratchGPR = regT1;
+    ASSERT(valueRegs.tagGPR() == scratchGPR);
+#endif
+    emitGetVirtualRegister(bytecode.m_value, valueRegs);
+
+    JumpList slowCases;
+    slowCases.append(branchIfNotCell(valueRegs));
+    slowCases.append(branchIfNotObject(valueRegs.payloadGPR()));
+
+    emitLoadPrototype(vm(), valueRegs.payloadGPR(), resultRegs, scratchGPR, slowCases);
+    addSlowCase(slowCases);
+
+    emitValueProfilingSite(bytecode.metadata(m_codeBlock));
+    emitPutVirtualRegister(bytecode.m_dst, resultRegs);
+}
+
 } // namespace JSC
 
 #endif // ENABLE(JIT)
index a28232e..9ff931c 100644 (file)
@@ -1386,6 +1386,12 @@ llintOpWithReturn(op_is_object, OpIsObject, macro (size, get, dispatch, return)
 end)
 
 
+macro loadInlineOffset(propertyOffsetAsInt, objectAndStorage, value)
+    addp sizeof JSObject - (firstOutOfLineOffset - 2) * 8, objectAndStorage
+    loadq (firstOutOfLineOffset - 2) * 8[objectAndStorage, propertyOffsetAsInt, 8], value
+end
+
+
 macro loadPropertyAtVariableOffset(propertyOffsetAsInt, objectAndStorage, value)
     bilt propertyOffsetAsInt, firstOutOfLineOffset, .isInline
     loadp JSObject::m_butterfly[objectAndStorage], objectAndStorage
@@ -1515,7 +1521,7 @@ llintOpWithProfile(op_get_prototype_of, OpGetPrototypeOf, macro (size, get, disp
 
 .opGetPrototypeOfPolyProto:
     move knownPolyProtoOffset, t1
-    loadPropertyAtVariableOffset(t1, t0, t3)
+    loadInlineOffset(t1, t0, t3)
     return(t3)
 end)