Title: [160670] trunk
Revision
160670
Author
[email protected]
Date
2013-12-16 16:01:01 -0800 (Mon, 16 Dec 2013)

Log Message

Cache getters and custom accessors on the prototype chain
https://bugs.webkit.org/show_bug.cgi?id=125602

Reviewed by Michael Saboff.

Source/_javascript_Core:

Support caching of custom getters and accessors on the prototype chain.
This is relatively trivial and just requires a little work compared to
the direct access mode as we're under more register pressure.

* bytecode/StructureStubInfo.h:
  Removed the unsued initGetByIdProto as it was confusing to still have it present.
* jit/Repatch.cpp:
(JSC::generateProtoChainAccessStub):
(JSC::tryCacheGetByID):
(JSC::tryBuildGetByIDList):

Tools:

Make sure bencher scripts also make noInline exist

* Scripts/bencher:

LayoutTests:

Added a bunch of new tests

* js/regress/chain-custom-getter-expected.txt: Added.
* js/regress/chain-custom-getter.html: Added.
* js/regress/chain-getter-access-expected.txt: Added.
* js/regress/chain-getter-access.html: Added.
* js/regress/proto-custom-getter-expected.txt: Added.
* js/regress/proto-custom-getter.html: Added.
* js/regress/proto-getter-access-expected.txt: Added.
* js/regress/proto-getter-access.html: Added.
* js/regress/resources/regress-pre.js:
  Made sure that noInline always exists (either using testRunner.neverInlineFunction
  or a no-op function if nothing else is available)
* js/regress/script-tests/chain-custom-getter.js: Added.
(foo):
* js/regress/script-tests/chain-getter-access.js: Added.
(o.get value):
(foo):
* js/regress/script-tests/proto-custom-getter.js: Added.
(foo):
* js/regress/script-tests/proto-getter-access.js: Added.
(o.get value):
(foo):
* js/regress/script-tests/simple-custom-getter.js: Added.
(cycles.30000.numberObject.Number.foo):
* js/regress/script-tests/simple-getter-access.js: Added.
(o.get value):
(foo):
* js/regress/simple-custom-getter-expected.txt: Added.
* js/regress/simple-custom-getter.html: Added.
* js/regress/simple-getter-access-expected.txt: Added.
* js/regress/simple-getter-access.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (160669 => 160670)


--- trunk/LayoutTests/ChangeLog	2013-12-16 23:54:09 UTC (rev 160669)
+++ trunk/LayoutTests/ChangeLog	2013-12-17 00:01:01 UTC (rev 160670)
@@ -1,3 +1,43 @@
+2013-12-16  Oliver Hunt  <[email protected]>
+
+        Cache getters and custom accessors on the prototype chain
+        https://bugs.webkit.org/show_bug.cgi?id=125602
+
+        Reviewed by Michael Saboff.
+
+        Added a bunch of new tests
+
+        * js/regress/chain-custom-getter-expected.txt: Added.
+        * js/regress/chain-custom-getter.html: Added.
+        * js/regress/chain-getter-access-expected.txt: Added.
+        * js/regress/chain-getter-access.html: Added.
+        * js/regress/proto-custom-getter-expected.txt: Added.
+        * js/regress/proto-custom-getter.html: Added.
+        * js/regress/proto-getter-access-expected.txt: Added.
+        * js/regress/proto-getter-access.html: Added.
+        * js/regress/resources/regress-pre.js:
+          Made sure that noInline always exists (either using testRunner.neverInlineFunction
+          or a no-op function if nothing else is available)
+        * js/regress/script-tests/chain-custom-getter.js: Added.
+        (foo):
+        * js/regress/script-tests/chain-getter-access.js: Added.
+        (o.get value):
+        (foo):
+        * js/regress/script-tests/proto-custom-getter.js: Added.
+        (foo):
+        * js/regress/script-tests/proto-getter-access.js: Added.
+        (o.get value):
+        (foo):
+        * js/regress/script-tests/simple-custom-getter.js: Added.
+        (cycles.30000.numberObject.Number.foo):
+        * js/regress/script-tests/simple-getter-access.js: Added.
+        (o.get value):
+        (foo):
+        * js/regress/simple-custom-getter-expected.txt: Added.
+        * js/regress/simple-custom-getter.html: Added.
+        * js/regress/simple-getter-access-expected.txt: Added.
+        * js/regress/simple-getter-access.html: Added.
+
 2013-12-16  Hans Muller  <[email protected]>
 
         [CSS Shapes] Add support for the computing the included intervals for a BoxShape

Added: trunk/LayoutTests/js/regress/chain-custom-getter-expected.txt (0 => 160670)


--- trunk/LayoutTests/js/regress/chain-custom-getter-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/chain-custom-getter-expected.txt	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,10 @@
+JSRegress/chain-custom-getter
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/chain-custom-getter.html (0 => 160670)


--- trunk/LayoutTests/js/regress/chain-custom-getter.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/chain-custom-getter.html	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/regress/chain-getter-access-expected.txt (0 => 160670)


--- trunk/LayoutTests/js/regress/chain-getter-access-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/chain-getter-access-expected.txt	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,10 @@
+JSRegress/chain-getter-access
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/chain-getter-access.html (0 => 160670)


--- trunk/LayoutTests/js/regress/chain-getter-access.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/chain-getter-access.html	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/regress/proto-custom-getter-expected.txt (0 => 160670)


--- trunk/LayoutTests/js/regress/proto-custom-getter-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/proto-custom-getter-expected.txt	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,10 @@
+JSRegress/proto-custom-getter
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/proto-custom-getter.html (0 => 160670)


--- trunk/LayoutTests/js/regress/proto-custom-getter.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/proto-custom-getter.html	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/regress/proto-getter-access-expected.txt (0 => 160670)


--- trunk/LayoutTests/js/regress/proto-getter-access-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/proto-getter-access-expected.txt	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,10 @@
+JSRegress/proto-getter-access
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/proto-getter-access.html (0 => 160670)


--- trunk/LayoutTests/js/regress/proto-getter-access.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/proto-getter-access.html	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Modified: trunk/LayoutTests/js/regress/resources/regress-pre.js (160669 => 160670)


--- trunk/LayoutTests/js/regress/resources/regress-pre.js	2013-12-16 23:54:09 UTC (rev 160669)
+++ trunk/LayoutTests/js/regress/resources/regress-pre.js	2013-12-17 00:01:01 UTC (rev 160670)
@@ -6,3 +6,11 @@
     _JSRegress_didSucceed = false;
     _JSRegress_oldOnError.apply(this, arguments);
 }
+
+if (typeof noInline == "undefined") {
+    if (window.testRunner)
+        noInline =window.testRunner.neverInlineFunction || function(){};
+    else
+        noInline = function(){}
+}
+

Added: trunk/LayoutTests/js/regress/script-tests/chain-custom-getter.js (0 => 160670)


--- trunk/LayoutTests/js/regress/script-tests/chain-custom-getter.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/chain-custom-getter.js	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,21 @@
+
+var cycles = 6000;
+var numberObject = { __proto__: Number}
+numberObject ={__proto__: numberObject}
+
+function foo() {
+    var result = 0;
+    var innerCycles = cycles;
+    var Number = numberObject;
+    for (var i = 0; i < innerCycles; ++i)
+        result += 0 | isNaN(Number.NaN);
+
+    return result;
+}
+
+noInline(foo);
+var result = 0;
+for (var i = 0; i < 1500; i++)
+    result += foo();
+if (result != i * cycles)
+    throw "Failed, result was " + (result) + " should be " + (i * cycles);

Added: trunk/LayoutTests/js/regress/script-tests/chain-getter-access.js (0 => 160670)


--- trunk/LayoutTests/js/regress/script-tests/chain-getter-access.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/chain-getter-access.js	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,30 @@
+var x = 1;
+var o = {
+    get value() { 
+        x ^= x * 3;
+        x = x | 1;
+        return x;
+    }
+}
+
+o = {__proto__: o}
+o = {__proto__: o}
+
+function foo(o) {
+    var result = 0;
+    for (var i = 0; i < 64; i++) {
+        result ^= o.value;
+        result |= 1
+    }
+    return result;
+}
+
+noInline(foo);
+var result = 0;
+for (var i = 0; i < 50000; ++i) {
+    result ^= foo(o);
+    result = result | 1;
+}
+
+if (result != -2004318071)
+    throw "Incorrect result: " + result + ". Should be -2004318071";

Added: trunk/LayoutTests/js/regress/script-tests/proto-custom-getter.js (0 => 160670)


--- trunk/LayoutTests/js/regress/script-tests/proto-custom-getter.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/proto-custom-getter.js	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,17 @@
+var cycles = 6000
+var numberObject = { __proto__: Number}
+function foo() {
+    var result = 0;
+    var innerCycles = cycles;
+    var Number = numberObject;
+    for (var i = 0; i < innerCycles; ++i)
+        result += 0 | isNaN(Number.NaN);
+
+    return result;
+}
+noInline(foo);
+var result = 0;
+for (var i = 0; i < 1500; i++)
+    result += foo();
+if (result != i * cycles)
+    throw "Failed, result was " + (result) + " should be " + (i * cycles)

Added: trunk/LayoutTests/js/regress/script-tests/proto-getter-access.js (0 => 160670)


--- trunk/LayoutTests/js/regress/script-tests/proto-getter-access.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/proto-getter-access.js	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,28 @@
+var x = 1;
+var o = {
+    get value() { 
+        x ^= x * 3;
+        x = x | 1;
+        return x;
+    }
+}
+
+o = {__proto__: o}
+
+function foo(o) {
+    var result = 0;
+    for (var i = 0; i < 64; i++) {
+        result ^= o.value;
+        result |= 1
+    }
+    return result;
+}
+
+noInline(foo);
+var result = 0;
+for (var i = 0; i < 50000; ++i) {
+    result ^= foo(o);
+    result = result | 1;
+}
+if (result != -2004318071)
+    throw "Incorrect result: " + result + ". Should be -2004318071";

Added: trunk/LayoutTests/js/regress/script-tests/simple-custom-getter.js (0 => 160670)


--- trunk/LayoutTests/js/regress/script-tests/simple-custom-getter.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/simple-custom-getter.js	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,17 @@
+var cycles = 20000
+var numberObject = Number
+function foo() {
+    var result = 0;
+    var innerCycles = cycles;
+    var Number = numberObject;
+    for (var i = 0; i < innerCycles; ++i)
+        result += 0 | isNaN(Number.NaN);
+
+    return result;
+}
+noInline(foo);
+var result = 0;
+for (var i = 0; i < 1500; i++)
+    result += foo();
+if (result != i * cycles)
+    throw "Failed, result was " + (result) + " should be " + (i * cycles)

Added: trunk/LayoutTests/js/regress/script-tests/simple-getter-access.js (0 => 160670)


--- trunk/LayoutTests/js/regress/script-tests/simple-getter-access.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/simple-getter-access.js	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,26 @@
+var x = 1;
+var o = {
+    get value() { 
+        x ^= x * 3;
+        x = x | 1;
+        return x;
+    }
+}
+
+function foo(o) {
+    var result = 0;
+    for (var i = 0; i < 128; i++) {
+        result ^= o.value;
+        result |= 1
+    }
+    return result;
+}
+
+noInline(foo);
+var result = 0;
+for (var i = 0; i < 40000; ++i) {
+    result ^= foo(o);
+    result = result | 1;
+}
+if (result != -2004318071)
+    throw "Incorrect result: " + result + ". Should be -2004318071";

Added: trunk/LayoutTests/js/regress/simple-custom-getter-expected.txt (0 => 160670)


--- trunk/LayoutTests/js/regress/simple-custom-getter-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/simple-custom-getter-expected.txt	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,10 @@
+JSRegress/simple-custom-getter
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/simple-custom-getter.html (0 => 160670)


--- trunk/LayoutTests/js/regress/simple-custom-getter.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/simple-custom-getter.html	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/regress/simple-getter-access-expected.txt (0 => 160670)


--- trunk/LayoutTests/js/regress/simple-getter-access-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/simple-getter-access-expected.txt	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,10 @@
+JSRegress/simple-getter-access
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/simple-getter-access.html (0 => 160670)


--- trunk/LayoutTests/js/regress/simple-getter-access.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/simple-getter-access.html	2013-12-17 00:01:01 UTC (rev 160670)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Modified: trunk/Source/_javascript_Core/ChangeLog (160669 => 160670)


--- trunk/Source/_javascript_Core/ChangeLog	2013-12-16 23:54:09 UTC (rev 160669)
+++ trunk/Source/_javascript_Core/ChangeLog	2013-12-17 00:01:01 UTC (rev 160670)
@@ -1,3 +1,21 @@
+2013-12-16  Oliver Hunt  <[email protected]>
+
+        Cache getters and custom accessors on the prototype chain
+        https://bugs.webkit.org/show_bug.cgi?id=125602
+
+        Reviewed by Michael Saboff.
+
+        Support caching of custom getters and accessors on the prototype chain.
+        This is relatively trivial and just requires a little work compared to
+        the direct access mode as we're under more register pressure.
+
+        * bytecode/StructureStubInfo.h:
+          Removed the unsued initGetByIdProto as it was confusing to still have it present.
+        * jit/Repatch.cpp:
+        (JSC::generateProtoChainAccessStub):
+        (JSC::tryCacheGetByID):
+        (JSC::tryBuildGetByIDList):
+
 2013-12-16  Mark Lam  <[email protected]>
 
         Change slow path result to take a void* instead of a ExecState*.

Modified: trunk/Source/_javascript_Core/bytecode/StructureStubInfo.h (160669 => 160670)


--- trunk/Source/_javascript_Core/bytecode/StructureStubInfo.h	2013-12-16 23:54:09 UTC (rev 160669)
+++ trunk/Source/_javascript_Core/bytecode/StructureStubInfo.h	2013-12-17 00:01:01 UTC (rev 160670)
@@ -119,15 +119,6 @@
         u.getByIdSelf.baseObjectStructure.set(vm, owner, baseObjectStructure);
     }
 
-    void initGetByIdProto(VM& vm, JSCell* owner, Structure* baseObjectStructure, Structure* prototypeStructure, bool isDirect)
-    {
-        accessType = access_get_by_id_proto;
-
-        u.getByIdProto.baseObjectStructure.set(vm, owner, baseObjectStructure);
-        u.getByIdProto.prototypeStructure.set(vm, owner, prototypeStructure);
-        u.getByIdProto.isDirect = isDirect;
-    }
-
     void initGetByIdChain(VM& vm, JSCell* owner, Structure* baseObjectStructure, StructureChain* chain, unsigned count, bool isDirect)
     {
         accessType = access_get_by_id_chain;

Modified: trunk/Source/_javascript_Core/jit/Repatch.cpp (160669 => 160670)


--- trunk/Source/_javascript_Core/jit/Repatch.cpp	2013-12-16 23:54:09 UTC (rev 160669)
+++ trunk/Source/_javascript_Core/jit/Repatch.cpp	2013-12-17 00:01:01 UTC (rev 160670)
@@ -220,21 +220,27 @@
     linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
 }
 
-static void generateProtoChainAccessStub(ExecState* exec, StructureStubInfo& stubInfo, StructureChain* chain, size_t count, PropertyOffset offset, Structure* structure, CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel, RefPtr<JITStubRoutine>& stubRoutine, const Identifier& propertyName)
+enum ProtoChainGenerationResult {
+    ProtoChainGenerationFailed,
+    ProtoChainGenerationSucceeded
+};
+
+static ProtoChainGenerationResult generateProtoChainAccessStub(ExecState*, const PropertySlot&, const Identifier&, StructureStubInfo&, StructureChain*, size_t, PropertyOffset, Structure*, CodeLocationLabel, CodeLocationLabel, RefPtr<JITStubRoutine>&) WARN_UNUSED_RETURN;
+static ProtoChainGenerationResult generateProtoChainAccessStub(ExecState* exec, const PropertySlot& slot, const Identifier& propertyName, StructureStubInfo& stubInfo, StructureChain* chain, size_t count, PropertyOffset offset, Structure* structure, CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel, RefPtr<JITStubRoutine>& stubRoutine)
 {
     VM* vm = &exec->vm();
-
-    MacroAssembler stubJit;
-        
     GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
 #if USE(JSVALUE32_64)
     GPRReg resultTagGPR = static_cast<GPRReg>(stubInfo.patch.valueTagGPR);
 #endif
     GPRReg resultGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
     GPRReg scratchGPR = TempRegisterSet(stubInfo.patch.usedRegisters).getFreeGPR();
-    bool needToRestoreScratch = false;
+    bool needToRestoreScratch = scratchGPR == InvalidGPRReg;
+    if (needToRestoreScratch && !slot.isCacheableValue())
+        return ProtoChainGenerationFailed;
     
-    if (scratchGPR == InvalidGPRReg) {
+    CCallHelpers stubJit(&exec->vm(), exec->codeBlock());
+    if (needToRestoreScratch) {
 #if USE(JSVALUE64)
         scratchGPR = AssemblyHelpers::selectScratchGPR(baseGPR, resultGPR);
 #else
@@ -266,36 +272,80 @@
         currStructure = it->get();
     }
     
-    if (isInlineOffset(offset)) {
+    bool isAccessor = slot.isCacheableGetter() || slot.isCacheableCustom();
+    if (isAccessor)
+        stubJit.move(baseGPR, scratchGPR);
+
+    if (!slot.isCacheableCustom()) {
+        if (isInlineOffset(offset)) {
 #if USE(JSVALUE64)
-        stubJit.load64(protoObject->locationForOffset(offset), resultGPR);
+            stubJit.load64(protoObject->locationForOffset(offset), resultGPR);
 #elif USE(JSVALUE32_64)
-        stubJit.move(MacroAssembler::TrustedImmPtr(protoObject->locationForOffset(offset)), resultGPR);
-        stubJit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR);
-        stubJit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR);
+            stubJit.move(MacroAssembler::TrustedImmPtr(protoObject->locationForOffset(offset)), resultGPR);
+            stubJit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR);
+            stubJit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR);
 #endif
-    } else {
-        stubJit.loadPtr(protoObject->butterflyAddress(), resultGPR);
+        } else {
+            stubJit.loadPtr(protoObject->butterflyAddress(), resultGPR);
 #if USE(JSVALUE64)
-        stubJit.load64(MacroAssembler::Address(resultGPR, offsetInButterfly(offset) * sizeof(WriteBarrier<Unknown>)), resultGPR);
+            stubJit.load64(MacroAssembler::Address(resultGPR, offsetInButterfly(offset) * sizeof(WriteBarrier<Unknown>)), resultGPR);
 #elif USE(JSVALUE32_64)
-        stubJit.load32(MacroAssembler::Address(resultGPR, offsetInButterfly(offset) * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR);
-        stubJit.load32(MacroAssembler::Address(resultGPR, offsetInButterfly(offset) * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR);
+            stubJit.load32(MacroAssembler::Address(resultGPR, offsetInButterfly(offset) * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR);
+            stubJit.load32(MacroAssembler::Address(resultGPR, offsetInButterfly(offset) * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR);
 #endif
+        }
     }
+    MacroAssembler::Call operationCall;
+    MacroAssembler::Call handlerCall;
+    FunctionPtr operationFunction;
+    MacroAssembler::Jump success, fail;
+    if (isAccessor) {
+        GPRReg callFrameRegister = static_cast<GPRReg>(stubInfo.patch.callFrameRegister);
+        if (slot.isCacheableGetter()) {
+            stubJit.setupArguments(callFrameRegister, scratchGPR, resultGPR);
+            operationFunction = operationCallGetter;
+        } else {
+            stubJit.move(MacroAssembler::TrustedImmPtr(protoObject), scratchGPR);
+            stubJit.setupArguments(callFrameRegister, scratchGPR,
+                MacroAssembler::TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()),
+                MacroAssembler::TrustedImmPtr(propertyName.impl()));
+            operationFunction = operationCallCustomGetter;
+        }
 
-    MacroAssembler::Jump success, fail;
-    
+        // Need to make sure that whenever this call is made in the future, we remember the
+        // place that we made it from. It just so happens to be the place that we are at
+        // right now!
+        stubJit.store32(MacroAssembler::TrustedImm32(exec->locationAsRawBits()),
+            CCallHelpers::tagFor(static_cast<VirtualRegister>(JSStack::ArgumentCount)));
+
+        operationCall = stubJit.call();
+#if USE(JSVALUE64)
+        stubJit.move(GPRInfo::returnValueGPR, resultGPR);
+#else
+        stubJit.setupResults(resultGPR, resultTagGPR);
+#endif
+        MacroAssembler::Jump noException = stubJit.emitExceptionCheck(CCallHelpers::InvertedExceptionCheck);
+
+        stubJit.setupArgumentsExecState();
+        handlerCall = stubJit.call();
+        stubJit.jumpToExceptionHandler();
+        
+        noException.link(&stubJit);
+    }
     emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
     
     LinkBuffer patchBuffer(*vm, &stubJit, exec->codeBlock());
     
     linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, successLabel, slowCaseLabel);
-    
+    if (isAccessor) {
+        patchBuffer.link(operationCall, operationFunction);
+        patchBuffer.link(handlerCall, lookupExceptionHandler);
+    }
     stubRoutine = FINALIZE_CODE_FOR_DFG_STUB(
         patchBuffer,
         ("DFG prototype chain access stub for %s, return point %p",
             toCString(*exec->codeBlock()).data(), successLabel.executableAddress()));
+    return ProtoChainGenerationSucceeded;
 }
 
 static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
@@ -393,25 +443,31 @@
     
     if (structure->isDictionary())
         return false;
+
+    if (!stubInfo.patch.registersFlushed) {
+        // We cannot do as much inline caching if the registers were not flushed prior to this GetById. In particular,
+        // non-Value cached properties require planting calls, which requires registers to have been flushed. Thus,
+        // if registers were not flushed, don't do non-Value caching.
+        if (!slot.isCacheableValue())
+            return false;
+    }
     
-    // FIXME: optimize getters and setters
-    if (!slot.isCacheableValue())
-        return false;
-    
     PropertyOffset offset = slot.cachedOffset();
     size_t count = normalizePrototypeChainForChainAccess(exec, baseValue, slot.slotBase(), propertyName, offset);
     if (count == InvalidPrototypeChain)
         return false;
 
     StructureChain* prototypeChain = structure->prototypeChain(exec);
+    if (generateProtoChainAccessStub(exec, slot, propertyName, stubInfo, prototypeChain, count, offset,
+        structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
+        stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase), stubInfo.stubRoutine) == ProtoChainGenerationFailed)
+        return false;
     
-    generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase), stubInfo.stubRoutine, propertyName);
-    
     RepatchBuffer repatchBuffer(codeBlock);
     replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code());
     repatchCall(repatchBuffer, stubInfo.callReturnLocation, operationGetByIdBuildList);
     
-    stubInfo.initGetByIdChain(*vm, codeBlock->ownerExecutable(), structure, prototypeChain, count, true);
+    stubInfo.initGetByIdChain(*vm, codeBlock->ownerExecutable(), structure, prototypeChain, count, slot.isCacheableValue());
     return true;
 }
 
@@ -629,9 +685,17 @@
     }
     
     if (baseValue.asCell()->structure()->typeInfo().prohibitsPropertyCaching()
-        || baseValue.asCell()->structure()->isDictionary()
-        || !slot.isCacheableValue())
+        || baseValue.asCell()->structure()->isDictionary())
         return false;
+    
+    if (!stubInfo.patch.registersFlushed) {
+        // We cannot do as much inline caching if the registers were not flushed prior to this GetById. In particular,
+        // non-Value cached properties require planting calls, which requires registers to have been flushed. Thus,
+        // if registers were not flushed, don't do non-Value caching.
+        if (!slot.isCacheableValue())
+            return false;
+    }
+    
 
     PropertyOffset offset = slot.cachedOffset();
     size_t count = normalizePrototypeChainForChainAccess(exec, baseValue, slot.slotBase(), ident, offset);
@@ -650,9 +714,12 @@
     
     RefPtr<JITStubRoutine> stubRoutine;
     
-    generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone), slowCase, stubRoutine, ident);
+    if (generateProtoChainAccessStub(exec, slot, ident, stubInfo, prototypeChain, count, offset, structure,
+        stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
+        slowCase, stubRoutine) == ProtoChainGenerationFailed)
+        return false;
     
-    polymorphicStructureList->list[listIndex].set(*vm, codeBlock->ownerExecutable(), stubRoutine, structure, true);
+    polymorphicStructureList->list[listIndex].set(*vm, codeBlock->ownerExecutable(), stubRoutine, structure, slot.isCacheableValue());
     
     patchJumpToGetByIdStub(codeBlock, stubInfo, stubRoutine.get());
     

Modified: trunk/Tools/ChangeLog (160669 => 160670)


--- trunk/Tools/ChangeLog	2013-12-16 23:54:09 UTC (rev 160669)
+++ trunk/Tools/ChangeLog	2013-12-17 00:01:01 UTC (rev 160670)
@@ -1,3 +1,14 @@
+2013-12-16  Oliver Hunt  <[email protected]>
+
+        Cache getters and custom accessors on the prototype chain
+        https://bugs.webkit.org/show_bug.cgi?id=125602
+
+        Reviewed by Michael Saboff.
+
+        Make sure bencher scripts also make noInline exist
+
+        * Scripts/bencher:
+
 2013-12-16  Anders Carlsson  <[email protected]>
 
         Fix crash when trying to load a null HTML string

Modified: trunk/Tools/Scripts/bencher (160669 => 160670)


--- trunk/Tools/Scripts/bencher	2013-12-16 23:54:09 UTC (rev 160669)
+++ trunk/Tools/Scripts/bencher	2013-12-17 00:01:01 UTC (rev 160670)
@@ -661,7 +661,9 @@
       else
         raise
       end
-      
+
+      doublePuts($stderr,file,"if (typeof noInline == 'undefined') noInline = function(){};")
+
       if benchDataPath
         doublePuts($stderr,file,"load(#{benchDataPath.inspect});")
         doublePuts($stderr,file,"gc();")
@@ -720,6 +722,7 @@
       doublePuts($stderr,file,"if (window.testRunner) {")
       doublePuts($stderr,file,"    testRunner.dumpAsText(window.enablePixelTesting);")
       doublePuts($stderr,file,"    testRunner.waitUntilDone();")
+      doublePuts($stderr,file,"    noInline = testRunner.neverInlineFunction || function(){};")
       doublePuts($stderr,file,"}")
       doublePuts($stderr,file,"")
       doublePuts($stderr,file,"function debug(msg)")
@@ -749,7 +752,7 @@
       if benchDataPath
         doublePuts($stderr,file,"        testFrame.contentDocument.write(\"<script src=""
       end
-      doublePuts($stderr,file,"        testFrame.contentDocument.write(\"<script type=\\\"text/_javascript_\\\">__bencher_before = Date.now();</script><script src="" type=\\\"text/_javascript_\\\">window.parent.reportResult(Date.now() - __bencher_before);</script></body></html>\");")
+      doublePuts($stderr,file,"        testFrame.contentDocument.write(\"<script type=\\\"text/_javascript_\\\">if (window.testRunner) noInline=window.testRunner.neverInlineFunction || function(){}; if (typeof noInline == 'undefined') noInline=function(){}; __bencher_before = Date.now();</script><script src="" type=\\\"text/_javascript_\\\">window.parent.reportResult(Date.now() - __bencher_before);</script></body></html>\");")
       doublePuts($stderr,file,"        testFrame.contentDocument.close();")
       doublePuts($stderr,file,"    }")
       doublePuts($stderr,file,"    __bencher_continuation = continuation;")
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to