Add IC support for arguments.length
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Apr 2016 19:37:04 +0000 (19:37 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Apr 2016 19:37:04 +0000 (19:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156389

Reviewed by Geoffrey Garen.
Source/JavaScriptCore:

This adds support for caching accesses to arguments.length for both DirectArguments and
ScopedArguments. In strict mode, we already cached these accesses since they were just
normal properties.

Amazingly, we also already supported caching of overridden arguments.length in both
DirectArguments and ScopedArguments. This is because when you override, the property gets
materialized as a normal JS property and the structure is changed.

This patch painstakingly preserves our previous caching of overridden length while
introducing caching of non-overridden length (i.e. the common case). In fact, we even cache
the case where it could either be overridden or not, since we just end up with an AccessCase
for each and they cascade to each other.

This is a >3x speed-up on microbenchmarks that do arguments.length in a polymorphic context.
Entirely monomorphic accesses were already handled by the DFG.

* bytecode/PolymorphicAccess.cpp:
(JSC::AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling):
(JSC::AccessCase::guardedByStructureCheck):
(JSC::AccessCase::generateWithGuard):
(JSC::AccessCase::generate):
(WTF::printInternal):
* bytecode/PolymorphicAccess.h:
* jit/ICStats.h:
* jit/JITOperations.cpp:
* jit/Repatch.cpp:
(JSC::tryCacheGetByID):
(JSC::tryCachePutByID):
(JSC::tryRepatchIn):
* tests/stress/direct-arguments-override-length-then-access-normal-length.js: Added.
(args):
(foo):
(result.foo):

LayoutTests:

* js/regress/direct-arguments-length-expected.txt: Added.
* js/regress/direct-arguments-length.html: Added.
* js/regress/direct-arguments-overridden-length-expected.txt: Added.
* js/regress/direct-arguments-overridden-length.html: Added.
* js/regress/direct-arguments-possibly-overridden-length-expected.txt: Added.
* js/regress/direct-arguments-possibly-overridden-length.html: Added.
* js/regress/scoped-arguments-length-expected.txt: Added.
* js/regress/scoped-arguments-length.html: Added.
* js/regress/scoped-arguments-overridden-length-expected.txt: Added.
* js/regress/scoped-arguments-overridden-length.html: Added.
* js/regress/scoped-arguments-possibly-overridden-length-expected.txt: Added.
* js/regress/scoped-arguments-possibly-overridden-length.html: Added.
* js/regress/script-tests/direct-arguments-length.js: Added.
(args):
* js/regress/script-tests/direct-arguments-overridden-length.js: Added.
(args):
* js/regress/script-tests/direct-arguments-possibly-overridden-length.js: Added.
(args1):
(args2):
* js/regress/script-tests/scoped-arguments-length.js: Added.
(args):
* js/regress/script-tests/scoped-arguments-overridden-length.js: Added.
(args):
* js/regress/script-tests/scoped-arguments-possibly-overridden-length.js: Added.
(args1):
(args2):

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/direct-arguments-length-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/direct-arguments-length.html [new file with mode: 0644]
LayoutTests/js/regress/direct-arguments-overridden-length-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/direct-arguments-overridden-length.html [new file with mode: 0644]
LayoutTests/js/regress/direct-arguments-possibly-overridden-length-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/direct-arguments-possibly-overridden-length.html [new file with mode: 0644]
LayoutTests/js/regress/scoped-arguments-length-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/scoped-arguments-length.html [new file with mode: 0644]
LayoutTests/js/regress/scoped-arguments-overridden-length-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/scoped-arguments-overridden-length.html [new file with mode: 0644]
LayoutTests/js/regress/scoped-arguments-possibly-overridden-length-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/scoped-arguments-possibly-overridden-length.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/direct-arguments-length.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/direct-arguments-overridden-length.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/direct-arguments-possibly-overridden-length.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/put-by-id-transition-with-indexing-header.js
LayoutTests/js/regress/script-tests/scoped-arguments-length.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/scoped-arguments-overridden-length.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/scoped-arguments-possibly-overridden-length.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp
Source/JavaScriptCore/bytecode/PolymorphicAccess.h
Source/JavaScriptCore/jit/ICStats.h
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/tests/stress/direct-arguments-override-length-then-access-normal-length.js [new file with mode: 0644]

index 77193c0..6544e70 100644 (file)
@@ -1,3 +1,37 @@
+2016-04-08  Filip Pizlo  <fpizlo@apple.com>
+
+        Add IC support for arguments.length
+        https://bugs.webkit.org/show_bug.cgi?id=156389
+
+        Reviewed by Geoffrey Garen.
+
+        * js/regress/direct-arguments-length-expected.txt: Added.
+        * js/regress/direct-arguments-length.html: Added.
+        * js/regress/direct-arguments-overridden-length-expected.txt: Added.
+        * js/regress/direct-arguments-overridden-length.html: Added.
+        * js/regress/direct-arguments-possibly-overridden-length-expected.txt: Added.
+        * js/regress/direct-arguments-possibly-overridden-length.html: Added.
+        * js/regress/scoped-arguments-length-expected.txt: Added.
+        * js/regress/scoped-arguments-length.html: Added.
+        * js/regress/scoped-arguments-overridden-length-expected.txt: Added.
+        * js/regress/scoped-arguments-overridden-length.html: Added.
+        * js/regress/scoped-arguments-possibly-overridden-length-expected.txt: Added.
+        * js/regress/scoped-arguments-possibly-overridden-length.html: Added.
+        * js/regress/script-tests/direct-arguments-length.js: Added.
+        (args):
+        * js/regress/script-tests/direct-arguments-overridden-length.js: Added.
+        (args):
+        * js/regress/script-tests/direct-arguments-possibly-overridden-length.js: Added.
+        (args1):
+        (args2):
+        * js/regress/script-tests/scoped-arguments-length.js: Added.
+        (args):
+        * js/regress/script-tests/scoped-arguments-overridden-length.js: Added.
+        (args):
+        * js/regress/script-tests/scoped-arguments-possibly-overridden-length.js: Added.
+        (args1):
+        (args2):
+
 2016-04-08  Joseph Pecoraro  <pecoraro@apple.com>
 
         Redefining a method of the same name hits an assertion
diff --git a/LayoutTests/js/regress/direct-arguments-length-expected.txt b/LayoutTests/js/regress/direct-arguments-length-expected.txt
new file mode 100644 (file)
index 0000000..beb6cfd
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/direct-arguments-length
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/direct-arguments-length.html b/LayoutTests/js/regress/direct-arguments-length.html
new file mode 100644 (file)
index 0000000..22df4a5
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/direct-arguments-length.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/direct-arguments-overridden-length-expected.txt b/LayoutTests/js/regress/direct-arguments-overridden-length-expected.txt
new file mode 100644 (file)
index 0000000..d7ea639
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/direct-arguments-overridden-length
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/direct-arguments-overridden-length.html b/LayoutTests/js/regress/direct-arguments-overridden-length.html
new file mode 100644 (file)
index 0000000..c2da1fa
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/direct-arguments-overridden-length.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/direct-arguments-possibly-overridden-length-expected.txt b/LayoutTests/js/regress/direct-arguments-possibly-overridden-length-expected.txt
new file mode 100644 (file)
index 0000000..c7b2905
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/direct-arguments-possibly-overridden-length
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/direct-arguments-possibly-overridden-length.html b/LayoutTests/js/regress/direct-arguments-possibly-overridden-length.html
new file mode 100644 (file)
index 0000000..314030d
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/direct-arguments-possibly-overridden-length.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/scoped-arguments-length-expected.txt b/LayoutTests/js/regress/scoped-arguments-length-expected.txt
new file mode 100644 (file)
index 0000000..31cd7b5
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/scoped-arguments-length
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/scoped-arguments-length.html b/LayoutTests/js/regress/scoped-arguments-length.html
new file mode 100644 (file)
index 0000000..6f69ce3
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/scoped-arguments-length.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/scoped-arguments-overridden-length-expected.txt b/LayoutTests/js/regress/scoped-arguments-overridden-length-expected.txt
new file mode 100644 (file)
index 0000000..e9a4f40
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/scoped-arguments-overridden-length
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/scoped-arguments-overridden-length.html b/LayoutTests/js/regress/scoped-arguments-overridden-length.html
new file mode 100644 (file)
index 0000000..089c3e3
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/scoped-arguments-overridden-length.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/scoped-arguments-possibly-overridden-length-expected.txt b/LayoutTests/js/regress/scoped-arguments-possibly-overridden-length-expected.txt
new file mode 100644 (file)
index 0000000..db81caf
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/scoped-arguments-possibly-overridden-length
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/scoped-arguments-possibly-overridden-length.html b/LayoutTests/js/regress/scoped-arguments-possibly-overridden-length.html
new file mode 100644 (file)
index 0000000..8229569
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/scoped-arguments-possibly-overridden-length.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/direct-arguments-length.js b/LayoutTests/js/regress/script-tests/direct-arguments-length.js
new file mode 100644 (file)
index 0000000..c5f1585
--- /dev/null
@@ -0,0 +1,14 @@
+(function() {
+    var args = (function() {
+        return arguments;
+    })(1, 2, 3, 4, 5);
+    
+    var array = [args, [1, 2, 3]];
+    
+    var result = 0;
+    for (var i = 0; i < 1000000; ++i)
+        result += array[i % array.length].length;
+    
+    if (result != 4000000)
+        throw "Error: bad result: " + result;
+})();
diff --git a/LayoutTests/js/regress/script-tests/direct-arguments-overridden-length.js b/LayoutTests/js/regress/script-tests/direct-arguments-overridden-length.js
new file mode 100644 (file)
index 0000000..c503921
--- /dev/null
@@ -0,0 +1,16 @@
+(function() {
+    var args = (function() {
+        var result = arguments;
+        result.length = 6;
+        return result;
+    })(1, 2, 3, 4, 5);
+    
+    var array = [args, [1, 2, 3]];
+    
+    var result = 0;
+    for (var i = 0; i < 1000000; ++i)
+        result += array[i % array.length].length;
+    
+    if (result != 4500000)
+        throw "Error: bad result: " + result;
+})();
diff --git a/LayoutTests/js/regress/script-tests/direct-arguments-possibly-overridden-length.js b/LayoutTests/js/regress/script-tests/direct-arguments-possibly-overridden-length.js
new file mode 100644 (file)
index 0000000..94dbcc3
--- /dev/null
@@ -0,0 +1,20 @@
+(function() {
+    var args1 = (function() {
+        return arguments;
+    })(1, 2, 3);
+    
+    var args2 = (function() {
+        var result = arguments;
+        result.length = 6;
+        return result;
+    })(1, 2, 3, 4, 5);
+    
+    var array = [args1, args2];
+    
+    var result = 0;
+    for (var i = 0; i < 1000000; ++i)
+        result += array[i % array.length].length;
+    
+    if (result != 4500000)
+        throw "Error: bad result: " + result;
+})();
index 37df715..f7c47d1 100644 (file)
@@ -3,7 +3,7 @@
         return {};
     };
     
-    for (var i = 0; i < 1000; ++i) {
+    for (var i = 0; i < 300; ++i) {
         var o;
         var n = 100;
         for (var j = 0; j < n; ++j) {
diff --git a/LayoutTests/js/regress/script-tests/scoped-arguments-length.js b/LayoutTests/js/regress/script-tests/scoped-arguments-length.js
new file mode 100644 (file)
index 0000000..a97fd90
--- /dev/null
@@ -0,0 +1,20 @@
+(function() {
+    var args = (function(a) {
+        (function() {
+            a++;
+        })();
+        return arguments;
+    })(1, 2, 3, 4, 5);
+    
+    if (args[0] != 2)
+        throw "Error: bad args: " + args;
+    
+    var array = [args, [1, 2, 3]];
+    
+    var result = 0;
+    for (var i = 0; i < 1000000; ++i)
+        result += array[i % array.length].length;
+    
+    if (result != 4000000)
+        throw "Error: bad result: " + result;
+})();
diff --git a/LayoutTests/js/regress/script-tests/scoped-arguments-overridden-length.js b/LayoutTests/js/regress/script-tests/scoped-arguments-overridden-length.js
new file mode 100644 (file)
index 0000000..f6cdd68
--- /dev/null
@@ -0,0 +1,22 @@
+(function() {
+    var args = (function(a) {
+        (function() {
+            a++;
+        })();
+        var result = arguments;
+        result.length = 6;
+        return result;
+    })(1, 2, 3, 4, 5);
+    
+    if (args[0] != 2)
+        throw "Error: bad args: " + args;
+    
+    var array = [args, [1, 2, 3]];
+    
+    var result = 0;
+    for (var i = 0; i < 1000000; ++i)
+        result += array[i % array.length].length;
+    
+    if (result != 4500000)
+        throw "Error: bad result: " + result;
+})();
diff --git a/LayoutTests/js/regress/script-tests/scoped-arguments-possibly-overridden-length.js b/LayoutTests/js/regress/script-tests/scoped-arguments-possibly-overridden-length.js
new file mode 100644 (file)
index 0000000..91a06e9
--- /dev/null
@@ -0,0 +1,32 @@
+(function() {
+    var args1 = (function(a) {
+        (function() {
+            a++;
+        })();
+        return arguments;
+    })(1, 2, 3);
+    
+    if (args1[0] != 2)
+        throw "Error: bad args1: " + args1;
+    
+    var args2 = (function(a) {
+        (function() {
+            a++;
+        })();
+        var result = arguments;
+        result.length = 6;
+        return result;
+    })(1, 2, 3, 4, 5);
+    
+    if (args2[0] != 2)
+        throw "Error: bad args2: " + args2;
+    
+    var array = [args1, args2];
+    
+    var result = 0;
+    for (var i = 0; i < 1000000; ++i)
+        result += array[i % array.length].length;
+    
+    if (result != 4500000)
+        throw "Error: bad result: " + result;
+})();
index 84a5265..8cb4ad8 100644 (file)
@@ -1,3 +1,44 @@
+2016-04-08  Filip Pizlo  <fpizlo@apple.com>
+
+        Add IC support for arguments.length
+        https://bugs.webkit.org/show_bug.cgi?id=156389
+
+        Reviewed by Geoffrey Garen.
+        
+        This adds support for caching accesses to arguments.length for both DirectArguments and
+        ScopedArguments. In strict mode, we already cached these accesses since they were just
+        normal properties.
+
+        Amazingly, we also already supported caching of overridden arguments.length in both
+        DirectArguments and ScopedArguments. This is because when you override, the property gets
+        materialized as a normal JS property and the structure is changed.
+        
+        This patch painstakingly preserves our previous caching of overridden length while
+        introducing caching of non-overridden length (i.e. the common case). In fact, we even cache
+        the case where it could either be overridden or not, since we just end up with an AccessCase
+        for each and they cascade to each other.
+
+        This is a >3x speed-up on microbenchmarks that do arguments.length in a polymorphic context.
+        Entirely monomorphic accesses were already handled by the DFG.
+
+        * bytecode/PolymorphicAccess.cpp:
+        (JSC::AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling):
+        (JSC::AccessCase::guardedByStructureCheck):
+        (JSC::AccessCase::generateWithGuard):
+        (JSC::AccessCase::generate):
+        (WTF::printInternal):
+        * bytecode/PolymorphicAccess.h:
+        * jit/ICStats.h:
+        * jit/JITOperations.cpp:
+        * jit/Repatch.cpp:
+        (JSC::tryCacheGetByID):
+        (JSC::tryCachePutByID):
+        (JSC::tryRepatchIn):
+        * tests/stress/direct-arguments-override-length-then-access-normal-length.js: Added.
+        (args):
+        (foo):
+        (result.foo):
+
 2016-04-08  Benjamin Poulain  <bpoulain@apple.com>
 
         UInt32ToNumber should have an Int52 path
index c78bef3..08bc101 100644 (file)
 #include "BinarySwitch.h"
 #include "CCallHelpers.h"
 #include "CodeBlock.h"
+#include "DirectArguments.h"
 #include "GetterSetter.h"
 #include "Heap.h"
 #include "JITOperations.h"
 #include "JSCInlines.h"
 #include "LinkBuffer.h"
+#include "ScopedArguments.h"
 #include "ScratchRegisterAllocator.h"
 #include "StructureStubClearingWatchpoint.h"
 #include "StructureStubInfo.h"
@@ -81,6 +83,7 @@ void AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling(co
             RELEASE_ASSERT(JITCode::isOptimizingJIT(jit->codeBlock()->jitType()));
 
         m_liveRegistersForCall = RegisterSet(m_liveRegistersToPreserveAtExceptionHandlingCallSite, allocator->usedRegisters());
+        m_liveRegistersForCall.merge(extra);
         m_liveRegistersForCall.exclude(RegisterSet::registersToNotSaveForJSCall());
         m_liveRegistersForCall.merge(extra);
     }
@@ -395,6 +398,8 @@ bool AccessCase::guardedByStructureCheck() const
     case MegamorphicLoad:
     case ArrayLength:
     case StringLength:
+    case DirectArgumentsLength:
+    case ScopedArgumentsLength:
         return false;
     default:
         return true;
@@ -522,6 +527,46 @@ void AccessCase::generateWithGuard(
         break;
     }
         
+    case DirectArgumentsLength: {
+        ASSERT(!viaProxy());
+        fallThrough.append(
+            jit.branch8(
+                CCallHelpers::NotEqual,
+                CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()),
+                CCallHelpers::TrustedImm32(DirectArgumentsType)));
+
+        fallThrough.append(
+            jit.branchTestPtr(
+                CCallHelpers::NonZero,
+                CCallHelpers::Address(baseGPR, DirectArguments::offsetOfOverrides())));
+        jit.load32(
+            CCallHelpers::Address(baseGPR, DirectArguments::offsetOfLength()),
+            valueRegs.payloadGPR());
+        jit.boxInt32(valueRegs.payloadGPR(), valueRegs, CCallHelpers::DoNotHaveTagRegisters);
+        state.succeed();
+        return;
+    }
+        
+    case ScopedArgumentsLength: {
+        ASSERT(!viaProxy());
+        fallThrough.append(
+            jit.branch8(
+                CCallHelpers::NotEqual,
+                CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()),
+                CCallHelpers::TrustedImm32(ScopedArgumentsType)));
+
+        fallThrough.append(
+            jit.branchTest8(
+                CCallHelpers::NonZero,
+                CCallHelpers::Address(baseGPR, ScopedArguments::offsetOfOverrodeThings())));
+        jit.load32(
+            CCallHelpers::Address(baseGPR, ScopedArguments::offsetOfTotalLength()),
+            valueRegs.payloadGPR());
+        jit.boxInt32(valueRegs.payloadGPR(), valueRegs, CCallHelpers::DoNotHaveTagRegisters);
+        state.succeed();
+        return;
+    }
+        
     case MegamorphicLoad: {
         UniquedStringImpl* key = ident.impl();
         unsigned hash = IdentifierRepHash::hash(key);
@@ -1036,7 +1081,7 @@ void AccessCase::generate(AccessGenerationState& state)
     }
 
     case Transition: {
-        // AccessCase::transition() should have returned null.
+        // AccessCase::transition() should have returned null if this wasn't true.
         RELEASE_ASSERT(GPRInfo::numberOfRegisters >= 6 || !structure()->outOfLineCapacity() || structure()->outOfLineCapacity() == newStructure()->outOfLineCapacity());
 
         if (InferredType* type = newStructure()->inferredTypeFor(ident.impl())) {
@@ -1257,7 +1302,7 @@ void AccessCase::generate(AccessGenerationState& state)
         state.succeed();
         return;
     }
-
+        
     case IntrinsicGetter: {
         RELEASE_ASSERT(isValidOffset(offset()));
 
@@ -1273,11 +1318,13 @@ void AccessCase::generate(AccessGenerationState& state)
         emitIntrinsicGetter(state);
         return;
     }
-    
+
+    case DirectArgumentsLength:
+    case ScopedArgumentsLength:
     case MegamorphicLoad:
-        // These need to be handled by generateWithGuard(), since the guard is part of the megamorphic load
-        // algorithm. We can be sure that nobody will call generate() directly for MegamorphicLoad since
-        // MegamorphicLoad is not guarded by a structure check.
+        // These need to be handled by generateWithGuard(), since the guard is part of the
+        // algorithm. We can be sure that nobody will call generate() directly for these since they
+        // are not guarded by structure checks.
         RELEASE_ASSERT_NOT_REACHED();
     }
     
@@ -1679,6 +1726,12 @@ void printInternal(PrintStream& out, AccessCase::AccessType type)
     case AccessCase::StringLength:
         out.print("StringLength");
         return;
+    case AccessCase::DirectArgumentsLength:
+        out.print("DirectArgumentsLength");
+        return;
+    case AccessCase::ScopedArgumentsLength:
+        out.print("ScopedArgumentsLength");
+        return;
     }
 
     RELEASE_ASSERT_NOT_REACHED();
index 92b6881..3229799 100644 (file)
@@ -68,7 +68,9 @@ public:
         InHit,
         InMiss,
         ArrayLength,
-        StringLength
+        StringLength,
+        DirectArgumentsLength,
+        ScopedArgumentsLength
     };
 
     static std::unique_ptr<AccessCase> tryGet(
index c53f619..c89b161 100644 (file)
@@ -40,6 +40,11 @@ namespace JSC {
 
 #define FOR_EACH_ICEVENT_KIND(macro) \
     macro(InvalidKind) \
+    macro(GetByIdAddAccessCase) \
+    macro(GetByIdReplaceWithJump) \
+    macro(GetByIdSelfPatch) \
+    macro(InAddAccessCase) \
+    macro(InReplaceWithJump) \
     macro(OperationGetById) \
     macro(OperationGetByIdGeneric) \
     macro(OperationGetByIdBuildList) \
@@ -58,7 +63,10 @@ namespace JSC {
     macro(OperationPutByIdStrictBuildList) \
     macro(OperationPutByIdNonStrictBuildList) \
     macro(OperationPutByIdDirectStrictBuildList) \
-    macro(OperationPutByIdDirectNonStrictBuildList)
+    macro(OperationPutByIdDirectNonStrictBuildList) \
+    macro(PutByIdAddAccessCase) \
+    macro(PutByIdReplaceWithJump) \
+    macro(PutByIdSelfPatch)
 
 class ICEvent {
 public:
index 1d746cb..3063c95 100644 (file)
@@ -188,6 +188,8 @@ EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState* exec, Struct
 
 EncodedJSValue JIT_OPERATION operationGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -203,6 +205,8 @@ EncodedJSValue JIT_OPERATION operationGetById(ExecState* exec, StructureStubInfo
 
 EncodedJSValue JIT_OPERATION operationGetByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -215,6 +219,8 @@ EncodedJSValue JIT_OPERATION operationGetByIdGeneric(ExecState* exec, EncodedJSV
 
 EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     Identifier ident = Identifier::fromUid(vm, uid);
@@ -232,6 +238,8 @@ EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState* exec, Structure
 
 EncodedJSValue JIT_OPERATION operationInOptimize(ExecState* exec, StructureStubInfo* stubInfo, JSCell* base, UniquedStringImpl* key)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -257,6 +265,8 @@ EncodedJSValue JIT_OPERATION operationInOptimize(ExecState* exec, StructureStubI
 
 EncodedJSValue JIT_OPERATION operationIn(ExecState* exec, StructureStubInfo* stubInfo, JSCell* base, UniquedStringImpl* key)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -274,6 +284,8 @@ EncodedJSValue JIT_OPERATION operationIn(ExecState* exec, StructureStubInfo* stu
 
 EncodedJSValue JIT_OPERATION operationGenericIn(ExecState* exec, JSCell* base, EncodedJSValue key)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
 
@@ -282,6 +294,8 @@ EncodedJSValue JIT_OPERATION operationGenericIn(ExecState* exec, JSCell* base, E
 
 void JIT_OPERATION operationPutByIdStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -297,6 +311,8 @@ void JIT_OPERATION operationPutByIdStrict(ExecState* exec, StructureStubInfo* st
 
 void JIT_OPERATION operationPutByIdNonStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -311,6 +327,8 @@ void JIT_OPERATION operationPutByIdNonStrict(ExecState* exec, StructureStubInfo*
 
 void JIT_OPERATION operationPutByIdDirectStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -325,6 +343,8 @@ void JIT_OPERATION operationPutByIdDirectStrict(ExecState* exec, StructureStubIn
 
 void JIT_OPERATION operationPutByIdDirectNonStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -339,6 +359,8 @@ void JIT_OPERATION operationPutByIdDirectNonStrict(ExecState* exec, StructureStu
 
 void JIT_OPERATION operationPutByIdStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -362,6 +384,8 @@ void JIT_OPERATION operationPutByIdStrictOptimize(ExecState* exec, StructureStub
 
 void JIT_OPERATION operationPutByIdNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -385,6 +409,8 @@ void JIT_OPERATION operationPutByIdNonStrictOptimize(ExecState* exec, StructureS
 
 void JIT_OPERATION operationPutByIdDirectStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
@@ -408,6 +434,8 @@ void JIT_OPERATION operationPutByIdDirectStrictOptimize(ExecState* exec, Structu
 
 void JIT_OPERATION operationPutByIdDirectNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
 {
+    SuperSamplerScope superSamplerScope(false);
+    
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
     
index 81fe3b0..71ceb89 100644 (file)
 #include "CallFrameShuffler.h"
 #include "DFGOperations.h"
 #include "DFGSpeculativeJIT.h"
+#include "DirectArguments.h"
 #include "FTLThunks.h"
 #include "GCAwareJITStubRoutine.h"
 #include "GetterSetter.h"
+#include "ICStats.h"
 #include "JIT.h"
 #include "JITInlines.h"
 #include "LinkBuffer.h"
 #include "JSCInlines.h"
 #include "PolymorphicAccess.h"
+#include "ScopedArguments.h"
 #include "ScratchRegisterAllocator.h"
 #include "StackAlignment.h"
 #include "StructureRareDataInlines.h"
@@ -241,11 +244,24 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
 
     std::unique_ptr<AccessCase> newCase;
 
-    if (isJSArray(baseValue) && propertyName == exec->propertyNames().length)
-        newCase = AccessCase::getLength(vm, codeBlock, AccessCase::ArrayLength);
-    else if (isJSString(baseValue) && propertyName == exec->propertyNames().length)
-        newCase = AccessCase::getLength(vm, codeBlock, AccessCase::StringLength);
-    else {
+    if (propertyName == vm.propertyNames->length) {
+        if (isJSArray(baseValue))
+            newCase = AccessCase::getLength(vm, codeBlock, AccessCase::ArrayLength);
+        else if (isJSString(baseValue))
+            newCase = AccessCase::getLength(vm, codeBlock, AccessCase::StringLength);
+        else if (DirectArguments* arguments = jsDynamicCast<DirectArguments*>(baseValue)) {
+            // If there were overrides, then we can handle this as a normal property load! Guarding
+            // this with such a check enables us to add an IC case for that load if needed.
+            if (!arguments->overrodeThings())
+                newCase = AccessCase::getLength(vm, codeBlock, AccessCase::DirectArgumentsLength);
+        } else if (ScopedArguments* arguments = jsDynamicCast<ScopedArguments*>(baseValue)) {
+            // Ditto.
+            if (!arguments->overrodeThings())
+                newCase = AccessCase::getLength(vm, codeBlock, AccessCase::ScopedArgumentsLength);
+        }
+    }
+    
+    if (!newCase) {
         if (!slot.isCacheable() && !slot.isUnset())
             return GiveUpOnCache;
 
@@ -275,6 +291,7 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
             && action == AttemptToCache
             && !structure->needImpurePropertyWatchpoint()
             && !loadTargetFromProxy) {
+            LOG_IC((ICEvent::GetByIdSelfPatch, structure->classInfo(), propertyName));
             structure->startWatchingPropertyForReplacements(vm, slot.cachedOffset());
             repatchByIdSelfAccess(codeBlock, stubInfo, structure, slot.cachedOffset(), appropriateOptimizingGetByIdFunction(kind), true);
             stubInfo.initGetByIdSelf(codeBlock, structure, slot.cachedOffset());
@@ -343,6 +360,8 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
         }
     }
 
+    LOG_IC((ICEvent::GetByIdAddAccessCase, baseValue.classInfoOrNull(), propertyName));
+
     AccessGenerationResult result = stubInfo.addAccessCase(codeBlock, propertyName, WTFMove(newCase));
 
     if (result.gaveUp())
@@ -350,6 +369,8 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
     if (result.madeNoChanges())
         return RetryCacheLater;
     
+    LOG_IC((ICEvent::GetByIdReplaceWithJump, baseValue.classInfoOrNull(), propertyName));
+
     RELEASE_ASSERT(result.code());
     replaceWithJump(stubInfo, result.code());
     
@@ -416,7 +437,9 @@ static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Str
                 && MacroAssembler::isPtrAlignedAddressOffset(maxOffsetRelativeToBase(slot.cachedOffset()))
                 && !structure->needImpurePropertyWatchpoint()
                 && !structure->inferredTypeFor(ident.impl())) {
-
+                
+                LOG_IC((ICEvent::PutByIdSelfPatch, structure->classInfo(), ident));
+                
                 repatchByIdSelfAccess(
                     codeBlock, stubInfo, structure, slot.cachedOffset(),
                     appropriateOptimizingPutByIdFunction(slot, putKind), false);
@@ -487,6 +510,8 @@ static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Str
         }
     }
 
+    LOG_IC((ICEvent::PutByIdAddAccessCase, structure->classInfo(), ident));
+    
     AccessGenerationResult result = stubInfo.addAccessCase(codeBlock, ident, WTFMove(newCase));
     
     if (result.gaveUp())
@@ -494,6 +519,8 @@ static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Str
     if (result.madeNoChanges())
         return RetryCacheLater;
 
+    LOG_IC((ICEvent::PutByIdReplaceWithJump, structure->classInfo(), ident));
+
     RELEASE_ASSERT(result.code());
     resetPutByIDCheckAndLoad(stubInfo);
     MacroAssembler::repatchJump(
@@ -544,6 +571,8 @@ static InlineCacheAction tryRepatchIn(
     if (!conditionSet.isValid())
         return GiveUpOnCache;
 
+    LOG_IC((ICEvent::InAddAccessCase, structure->classInfo(), ident));
+
     std::unique_ptr<AccessCase> newCase = AccessCase::in(
         vm, codeBlock, wasFound ? AccessCase::InHit : AccessCase::InMiss, structure, conditionSet);
 
@@ -553,6 +582,8 @@ static InlineCacheAction tryRepatchIn(
     if (result.madeNoChanges())
         return RetryCacheLater;
 
+    LOG_IC((ICEvent::InReplaceWithJump, structure->classInfo(), ident));
+
     RELEASE_ASSERT(result.code());
     MacroAssembler::repatchJump(
         stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump),
diff --git a/Source/JavaScriptCore/tests/stress/direct-arguments-override-length-then-access-normal-length.js b/Source/JavaScriptCore/tests/stress/direct-arguments-override-length-then-access-normal-length.js
new file mode 100644 (file)
index 0000000..fd3a7bf
--- /dev/null
@@ -0,0 +1,25 @@
+(function() {
+    var args = (function() {
+        var result = arguments;
+        result.length = 6;
+        return result;
+    })(1, 2, 3, 4, 5);
+    
+    var array = [args, [1, 2, 3]];
+    
+    function foo(thing) {
+        return thing.length;
+    }
+    noInline(foo);
+    
+    var result = 0;
+    for (var i = 0; i < 10000; ++i)
+        result += foo(array[i % array.length]);
+    
+    if (result != 45000)
+        throw "Error: bad result: " + result;
+    
+    var result = foo((function() { return arguments; })(1, 2, 3, 4));
+    if (result != 4)
+        throw "Error: bad result: " + result;
+})();