LLInt64 should have typed array fast paths for get_by_val
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 May 2016 17:08:45 +0000 (17:08 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 May 2016 17:08:45 +0000 (17:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=157931

Reviewed by Keith Miller.

Source/JavaScriptCore:

I think that the LLInt should be able to access typed arrays more quickly than it does now.
Ideally we would have fast paths for every major typed array operation and we would use
inline cache optimizations. I don't want to do this all in one go, so my plan is to
incrementally add support for this as time allows.

This change just adds the easy typed array fast paths for get_by_val in the 64-bit version
of LLInt.

Another bug, https://bugs.webkit.org/show_bug.cgi?id=157922, tracks the overall task of
adding all typed array fast paths to both versions of the LLInt.

This is a 30% speed-up on typed array benchmarks in LLInt. This is not a speed-up when the
JITs are enabled.

* llint/LLIntData.cpp:
(JSC::LLInt::Data::performAssertions):
* llint/LLIntOffsetsExtractor.cpp:
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter64.asm:
* offlineasm/backends.rb:
* runtime/JSArrayBufferView.h:
* runtime/JSType.h:

LayoutTests:

* js/regress/get_by_val-Int32Array-expected.txt: Added.
* js/regress/get_by_val-Int32Array.html: Added.
* js/regress/script-tests/get_by_val-Int32Array.js: Added.

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

12 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/get_by_val-Int32Array-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/get_by_val-Int32Array.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/get_by_val-Int32Array.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/llint/LLIntData.cpp
Source/JavaScriptCore/llint/LLIntOffsetsExtractor.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/offlineasm/backends.rb
Source/JavaScriptCore/runtime/JSArrayBufferView.h
Source/JavaScriptCore/runtime/JSType.h

index a65a40f..0a4e135 100644 (file)
@@ -1,3 +1,14 @@
+2016-05-24  Filip Pizlo  <fpizlo@apple.com>
+
+        LLInt64 should have typed array fast paths for get_by_val
+        https://bugs.webkit.org/show_bug.cgi?id=157931
+
+        Reviewed by Keith Miller.
+
+        * js/regress/get_by_val-Int32Array-expected.txt: Added.
+        * js/regress/get_by_val-Int32Array.html: Added.
+        * js/regress/script-tests/get_by_val-Int32Array.js: Added.
+
 2016-05-24  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         Unreviewed GTK+ gardening. Rebaseline several tests after r200116.
diff --git a/LayoutTests/js/regress/get_by_val-Int32Array-expected.txt b/LayoutTests/js/regress/get_by_val-Int32Array-expected.txt
new file mode 100644 (file)
index 0000000..8d171dd
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/get_by_val-Int32Array
+
+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/get_by_val-Int32Array.html b/LayoutTests/js/regress/get_by_val-Int32Array.html
new file mode 100644 (file)
index 0000000..eff9b44
--- /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/get_by_val-Int32Array.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/get_by_val-Int32Array.js b/LayoutTests/js/regress/script-tests/get_by_val-Int32Array.js
new file mode 100644 (file)
index 0000000..4145cd8
--- /dev/null
@@ -0,0 +1,10 @@
+(function() {
+    var array = new Int32Array(42);
+    for (var i = 0; i < 42; ++i)
+        array[i] = i;
+    var result = 0;
+    for (var i = 0; i < 100000; ++i)
+        result += array[i % array.length];
+    if (result != 2049960)
+        throw "Error: bad result: " + result;
+})();
index 870e639..7feb177 100644 (file)
@@ -1,3 +1,33 @@
+2016-05-24  Filip Pizlo  <fpizlo@apple.com>
+
+        LLInt64 should have typed array fast paths for get_by_val
+        https://bugs.webkit.org/show_bug.cgi?id=157931
+
+        Reviewed by Keith Miller.
+
+        I think that the LLInt should be able to access typed arrays more quickly than it does now.
+        Ideally we would have fast paths for every major typed array operation and we would use
+        inline cache optimizations. I don't want to do this all in one go, so my plan is to
+        incrementally add support for this as time allows.
+        
+        This change just adds the easy typed array fast paths for get_by_val in the 64-bit version
+        of LLInt.
+        
+        Another bug, https://bugs.webkit.org/show_bug.cgi?id=157922, tracks the overall task of
+        adding all typed array fast paths to both versions of the LLInt.
+        
+        This is a 30% speed-up on typed array benchmarks in LLInt. This is not a speed-up when the
+        JITs are enabled.
+
+        * llint/LLIntData.cpp:
+        (JSC::LLInt::Data::performAssertions):
+        * llint/LLIntOffsetsExtractor.cpp:
+        * llint/LowLevelInterpreter.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * offlineasm/backends.rb:
+        * runtime/JSArrayBufferView.h:
+        * runtime/JSType.h:
+
 2016-05-24  Saam barati  <sbarati@apple.com> and Yusuke Suzuki <utatane.tea@gmail.com>
 
         ThisTDZMode is no longer needed
index ee5f984..1bae262 100644 (file)
@@ -148,6 +148,15 @@ void Data::performAssertions(VM& vm)
     STATIC_ASSERT(ObjectType == 20);
     STATIC_ASSERT(FinalObjectType == 21);
     STATIC_ASSERT(JSFunctionType == 23);
+    STATIC_ASSERT(Int8ArrayType == 100);
+    STATIC_ASSERT(Int16ArrayType == 101);
+    STATIC_ASSERT(Int32ArrayType == 102);
+    STATIC_ASSERT(Uint8ArrayType == 103);
+    STATIC_ASSERT(Uint8ClampedArrayType == 104);
+    STATIC_ASSERT(Uint16ArrayType == 105);
+    STATIC_ASSERT(Uint32ArrayType == 106);
+    STATIC_ASSERT(Float32ArrayType == 107);
+    STATIC_ASSERT(Float64ArrayType == 108);
     STATIC_ASSERT(MasqueradesAsUndefined == 1);
     STATIC_ASSERT(ImplementsDefaultHasInstance == 2);
     STATIC_ASSERT(FirstConstantRegisterIndex == 0x40000000);
index 57d29c8..8eb9a50 100644 (file)
@@ -35,9 +35,9 @@
 #include "Heap.h"
 #include "Interpreter.h"
 #include "JSArray.h"
+#include "JSArrayBufferView.h"
 #include "JSCell.h"
 #include "JSFunction.h"
-#include "VM.h"
 #include "JSEnvironmentRecord.h"
 #include "JSGlobalObject.h"
 #include "JSModuleRecord.h"
@@ -56,6 +56,7 @@
 #include "TypeProfilerLog.h"
 #include "VMEntryRecord.h"
 #include "ValueProfile.h"
+#include "VM.h"
 #include "Watchdog.h"
 #include <wtf/text/StringImpl.h>
 
index 4d07a36..145003d 100644 (file)
@@ -333,6 +333,21 @@ const ObjectType = 20
 const FinalObjectType = 21
 const JSFunctionType = 23
 
+# The typed array types need to be numbered in a particular order because of the manually written
+# switch statement in get_by_val and put_by_val.
+const Int8ArrayType = 100
+const Int16ArrayType = 101
+const Int32ArrayType = 102
+const Uint8ArrayType = 103
+const Uint8ClampedArrayType = 104
+const Uint16ArrayType = 105
+const Uint32ArrayType = 106
+const Float32ArrayType = 107
+const Float64ArrayType = 108
+
+const FirstArrayType = Int8ArrayType
+const LastArrayType = Float64ArrayType
+
 # Type flags constants.
 const MasqueradesAsUndefined = 1
 const ImplementsDefaultHasInstance = 2
index ea5e28b..46e5616 100644 (file)
@@ -1393,6 +1393,23 @@ _llint_op_put_by_id:
     callSlowPath(_llint_slow_path_put_by_id)
     dispatch(9)
 
+macro finishGetByVal(result, scratch)
+    loadisFromInstruction(1, scratch)
+    storeq result, [cfr, scratch, 8]
+    valueProfile(result, 5, scratch)
+    dispatch(6)
+end
+
+macro finishIntGetByVal(result, scratch)
+    orq tagTypeNumber, result
+    finishGetByVal(result, scratch)
+end
+
+macro finishDoubleGetByVal(result, scratch1, scratch2)
+    fd2q result, scratch1
+    subq tagTypeNumber, scratch1
+    finishGetByVal(scratch1, scratch2)
+end
 
 _llint_op_get_by_val:
     traceExecution()
@@ -1428,7 +1445,7 @@ _llint_op_get_by_val:
     
 .opGetByValNotDouble:
     subi ArrayStorageShape, t2
-    bia t2, SlowPutArrayStorageShape - ArrayStorageShape, .opGetByValSlow
+    bia t2, SlowPutArrayStorageShape - ArrayStorageShape, .opGetByValNotIndexedStorage
     biaeq t1, -sizeof IndexingHeader + IndexingHeader::u.lengths.vectorLength[t3], .opGetByValOutOfBounds
     loadisFromInstruction(1, t0)
     loadq ArrayStorage::m_vector[t3, t1, 8], t2
@@ -1442,6 +1459,73 @@ _llint_op_get_by_val:
 .opGetByValOutOfBounds:
     loadpFromInstruction(4, t0)
     storeb 1, ArrayProfile::m_outOfBounds[t0]
+
+.opGetByValNotIndexedStorage:
+    # First lets check if we even have a typed array. This lets us do some boilerplate up front.
+    loadb JSCell::m_type[t0], t2
+    subi FirstArrayType, t2
+    bia t2, LastArrayType - FirstArrayType, .opGetByValSlow
+    
+    # Sweet, now we know that we have a typed array. Do some basic things now.
+    loadp JSArrayBufferView::m_vector[t0], t3
+    biaeq t1, JSArrayBufferView::m_length[t0], .opGetByValSlow
+    
+    # Now bisect through the various types. Note that we can treat Uint8ArrayType and
+    # Uint8ClampedArrayType the same.
+    bia t2, Uint8ClampedArrayType - FirstArrayType, .opGetByValAboveUint8ClampedArray
+    
+    # We have one of Int8ArrayType .. Uint8ClampedArrayType.
+    bia t2, Int16ArrayType - FirstArrayType, .opGetByValInt32ArrayOrUint8Array
+    
+    # We have one of Int8ArrayType or Int16ArrayType
+    bineq t2, Int8ArrayType - FirstArrayType, .opGetByValInt16Array
+    
+    # We have Int8ArrayType
+    loadbs [t3, t1], t0
+    finishIntGetByVal(t0, t1)
+
+.opGetByValInt16Array:
+    loadhs [t3, t1, 2], t0
+    finishIntGetByVal(t0, t1)
+
+.opGetByValInt32ArrayOrUint8Array:
+    # We have one of Int16Array, Uint8Array, or Uint8ClampedArray.
+    bieq t2, Int32ArrayType - FirstArrayType, .opGetByValInt32Array
+    
+    # We have either Uint8Array or Uint8ClampedArray. They behave the same so that's cool.
+    loadb [t3, t1], t0
+    finishIntGetByVal(t0, t1)
+
+.opGetByValInt32Array:
+    loadi [t3, t1, 4], t0
+    finishIntGetByVal(t0, t1)
+
+.opGetByValAboveUint8ClampedArray:
+    # We have one of Uint16ArrayType .. Float64ArrayType.
+    bia t2, Uint32ArrayType - FirstArrayType, .opGetByValAboveUint32Array
+    
+    # We have either Uint16ArrayType or Uint32ArrayType.
+    bieq t2, Uint32ArrayType - FirstArrayType, .opGetByValUint32Array
+
+    # We have Uint16ArrayType.
+    loadh [t3, t1, 2], t0
+    finishIntGetByVal(t0, t1)
+
+.opGetByValUint32Array:
+    # This is the hardest part because of large unsigned values.
+    loadi [t3, t1, 4], t0
+    bilt t0, 0, .opGetByValSlow # This case is still awkward to implement in LLInt.
+    finishIntGetByVal(t0, t1)
+
+.opGetByValAboveUint32Array:
+    # We have one of Float32ArrayType or Float64ArrayType. Sadly, we cannot handle Float32Array
+    # inline yet. That would require some offlineasm changes.
+    bieq t2, Float32ArrayType - FirstArrayType, .opGetByValSlow
+
+    # We have Float64ArrayType.
+    loadd [t3, t1, 8], ft0
+    finishDoubleGetByVal(ft0, t0, t1)
+
 .opGetByValSlow:
     callSlowPath(_llint_slow_path_get_by_val)
     dispatch(6)
index 2744419..eb32756 100644 (file)
@@ -98,14 +98,23 @@ def validBackends
     $validBackends.keys
 end
 
+class LoweringError < StandardError
+    attr_reader :originString
+    
+    def initialize(e, originString)
+        super "#{e} (due to #{originString})"
+        @originString = originString
+        set_backtrace e.backtrace
+    end
+end
+
 class Node
     def lower(name)
         begin
             $activeBackend = name
             send("lower" + name)
         rescue => e
-            e.message << "At #{codeOriginString}"
-            raise e
+            raise LoweringError.new(e, codeOriginString)
         end
     end
 end
index 7707bc9..96132c2 100644 (file)
@@ -30,6 +30,8 @@
 
 namespace JSC {
 
+class LLIntOffsetsExtractor;
+
 // This class serves two purposes:
 //
 // 1) It provides those parts of JSGenericTypedArrayView that don't depend
@@ -173,6 +175,8 @@ private:
     static void finalize(JSCell*);
 
 protected:
+    friend class LLIntOffsetsExtractor;
+
     ArrayBuffer* existingBufferInButterfly();
 
     CopyBarrier<char> m_vector; // this is really a void*, but void would not work here.
index 21934bc..c5d8b88 100644 (file)
@@ -63,7 +63,7 @@ enum JSType : uint8_t {
     DirectArgumentsType,
     ScopedArgumentsType,
 
-    Int8ArrayType,
+    Int8ArrayType = 100,
     Int16ArrayType,
     Int32ArrayType,
     Uint8ArrayType,