[JSC] Inline JSC::toInt32 to improve kraken
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 11 Jun 2016 05:31:48 +0000 (05:31 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 11 Jun 2016 05:31:48 +0000 (05:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=158619

Reviewed by Mark Lam.

Several kraken benchmarks show that JSC::toInt32 is frequently called.
For example, stanford-crypto-pbkdf2 reports that the hottest runtime function is JSC::toInt32.

The data is below (taken by Linux perf tools).
5.50%  jsc      libJavaScriptCore.so.1.0.0  [.] _ZN3JSC7toInt32Ed
3.96%  jsc      libJavaScriptCore.so.1.0.0  [.] _ZN3JSC20arrayProtoFuncConcatEPNS_9ExecStateE
2.48%  jsc      libJavaScriptCore.so.1.0.0  [.] _ZN3JSC19arrayProtoFuncSliceEPNS_9ExecStateE
1.69%  jsc      libJavaScriptCore.so.1.0.0  [.] _ZNK3JSC9Structure27holesMustForwardToPrototypeERNS_2VME

This is because of CommonSlowPaths' bit operations's JSValue::toInt32.
Due to the slow path, in `value | 0`, `value` may be a double number value. In that case, JSC::toInt32 is called.

While JSC::toIn32 is hot, the function itself is very small. It's worth inlining.

This change offers the following kraken improvements.

                                                 baseline                  patched
Kraken:
   audio-beat-detection                       47.492+-1.701             46.657+-1.232           might be 1.0179x faster
   stanford-crypto-aes                        43.669+-0.210      ^      42.862+-0.115         ^ definitely 1.0188x faster
   stanford-crypto-ccm                        45.213+-1.424             44.490+-1.290           might be 1.0162x faster
   stanford-crypto-pbkdf2                    107.665+-0.581      ^     106.229+-0.807         ^ definitely 1.0135x faster

This patch only focused on the call to toInt32 from the runtime functions.
So JSC::toInt32 calls from the baseline / DFG remain.
We ensure that JIT code uses operationToInt32 instead of JSC::toInt32 since JSC::toInt32 is now marked as ALWAYS_INLINE.
Linux perf profiler also finds that this `operationToInt32` is frequently called in the above benchmarks.
It may be good to introduce asm emit for that instead of calling JSC::toInt32 operation in the separated patch.

* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileValueToInt32):
(JSC::DFG::SpeculativeJIT::compilePutByValForIntTypedArray):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::doubleToInt32):
(JSC::FTL::DFG::LowerDFGToB3::sensibleDoubleToInt32):
* runtime/JSCJSValue.cpp:
(JSC::toInt32): Deleted.
* runtime/JSCJSValueInlines.h:
* runtime/MathCommon.cpp:
(JSC::operationToInt32):
* runtime/MathCommon.h:
(JSC::toInt32):

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/JSCJSValue.cpp
Source/JavaScriptCore/runtime/JSCJSValueInlines.h
Source/JavaScriptCore/runtime/MathCommon.cpp
Source/JavaScriptCore/runtime/MathCommon.h

index a9f7ad4..f52f48d 100644 (file)
@@ -1,3 +1,53 @@
+2016-06-10  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Inline JSC::toInt32 to improve kraken
+        https://bugs.webkit.org/show_bug.cgi?id=158619
+
+        Reviewed by Mark Lam.
+
+        Several kraken benchmarks show that JSC::toInt32 is frequently called.
+        For example, stanford-crypto-pbkdf2 reports that the hottest runtime function is JSC::toInt32.
+
+        The data is below (taken by Linux perf tools).
+        5.50%  jsc      libJavaScriptCore.so.1.0.0  [.] _ZN3JSC7toInt32Ed
+        3.96%  jsc      libJavaScriptCore.so.1.0.0  [.] _ZN3JSC20arrayProtoFuncConcatEPNS_9ExecStateE
+        2.48%  jsc      libJavaScriptCore.so.1.0.0  [.] _ZN3JSC19arrayProtoFuncSliceEPNS_9ExecStateE
+        1.69%  jsc      libJavaScriptCore.so.1.0.0  [.] _ZNK3JSC9Structure27holesMustForwardToPrototypeERNS_2VME
+
+        This is because of CommonSlowPaths' bit operations's JSValue::toInt32.
+        Due to the slow path, in `value | 0`, `value` may be a double number value. In that case, JSC::toInt32 is called.
+
+        While JSC::toIn32 is hot, the function itself is very small. It's worth inlining.
+
+        This change offers the following kraken improvements.
+
+                                                         baseline                  patched
+        Kraken:
+           audio-beat-detection                       47.492+-1.701             46.657+-1.232           might be 1.0179x faster
+           stanford-crypto-aes                        43.669+-0.210      ^      42.862+-0.115         ^ definitely 1.0188x faster
+           stanford-crypto-ccm                        45.213+-1.424             44.490+-1.290           might be 1.0162x faster
+           stanford-crypto-pbkdf2                    107.665+-0.581      ^     106.229+-0.807         ^ definitely 1.0135x faster
+
+        This patch only focused on the call to toInt32 from the runtime functions.
+        So JSC::toInt32 calls from the baseline / DFG remain.
+        We ensure that JIT code uses operationToInt32 instead of JSC::toInt32 since JSC::toInt32 is now marked as ALWAYS_INLINE.
+        Linux perf profiler also finds that this `operationToInt32` is frequently called in the above benchmarks.
+        It may be good to introduce asm emit for that instead of calling JSC::toInt32 operation in the separated patch.
+
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileValueToInt32):
+        (JSC::DFG::SpeculativeJIT::compilePutByValForIntTypedArray):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::doubleToInt32):
+        (JSC::FTL::DFG::LowerDFGToB3::sensibleDoubleToInt32):
+        * runtime/JSCJSValue.cpp:
+        (JSC::toInt32): Deleted.
+        * runtime/JSCJSValueInlines.h:
+        * runtime/MathCommon.cpp:
+        (JSC::operationToInt32):
+        * runtime/MathCommon.h:
+        (JSC::toInt32):
+
 2016-06-10  Filip Pizlo  <fpizlo@apple.com>
 
         The backend should be happy to compile Unreachable even if AI didn't prove it to be unreachable
index 73126b6..4cb68e2 100644 (file)
@@ -2120,7 +2120,7 @@ void SpeculativeJIT::compileValueToInt32(Node* node)
         GPRReg gpr = result.gpr();
         JITCompiler::Jump notTruncatedToInteger = m_jit.branchTruncateDoubleToInt32(fpr, gpr, JITCompiler::BranchIfTruncateFailed);
         
-        addSlowPathGenerator(slowPathCall(notTruncatedToInteger, this, toInt32, gpr, fpr, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded));
+        addSlowPathGenerator(slowPathCall(notTruncatedToInteger, this, operationToInt32, gpr, fpr, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded));
         
         int32Result(gpr, node);
         return;
@@ -2171,7 +2171,7 @@ void SpeculativeJIT::compileValueToInt32(Node* node)
             unboxDouble(gpr, resultGpr, fpr);
 
             silentSpillAllRegisters(resultGpr);
-            callOperation(toInt32, resultGpr, fpr);
+            callOperation(operationToInt32, resultGpr, fpr);
             silentFillAllRegisters(resultGpr);
 
             converted.append(m_jit.jump());
@@ -2230,7 +2230,7 @@ void SpeculativeJIT::compileValueToInt32(Node* node)
                 unboxDouble(tagGPR, payloadGPR, fpr, scratch.fpr());
 
                 silentSpillAllRegisters(resultGpr);
-                callOperation(toInt32, resultGpr, fpr);
+                callOperation(operationToInt32, resultGpr, fpr);
                 silentFillAllRegisters(resultGpr);
 
                 converted.append(m_jit.jump());
@@ -2770,7 +2770,7 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(GPRReg base, GPRReg propert
                 MacroAssembler::Jump failed = m_jit.branchTruncateDoubleToInt32(
                     fpr, gpr, MacroAssembler::BranchIfTruncateFailed);
                 
-                addSlowPathGenerator(slowPathCall(failed, this, toInt32, gpr, fpr, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded));
+                addSlowPathGenerator(slowPathCall(failed, this, operationToInt32, gpr, fpr, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded));
                 
                 fixed.link(&m_jit);
                 value.adopt(result);
index dde7c77..78531b1 100644 (file)
@@ -9106,7 +9106,7 @@ private:
         m_out.jump(continuation);
         
         m_out.appendTo(slowPath, continuation);
-        results.append(m_out.anchor(m_out.call(m_out.int32, m_out.operation(toInt32), doubleValue)));
+        results.append(m_out.anchor(m_out.call(m_out.int32, m_out.operation(operationToInt32), doubleValue)));
         m_out.jump(continuation);
         
         m_out.appendTo(continuation, lastNext);
@@ -9135,7 +9135,7 @@ private:
         
         LBasicBlock lastNext = m_out.appendTo(slowPath, continuation);
         ValueFromBlock slowResult = m_out.anchor(
-            m_out.call(m_out.int32, m_out.operation(toInt32), doubleValue));
+            m_out.call(m_out.int32, m_out.operation(operationToInt32), doubleValue));
         m_out.jump(continuation);
         
         m_out.appendTo(continuation, lastNext);
index ca4f558..ddf3b5f 100644 (file)
@@ -314,51 +314,6 @@ void JSValue::dumpForBacktrace(PrintStream& out) const
         out.print("INVALID");
 }
 
-// This in the ToInt32 operation is defined in section 9.5 of the ECMA-262 spec.
-// Note that this operation is identical to ToUInt32 other than to interpretation
-// of the resulting bit-pattern (as such this metod is also called to implement
-// ToUInt32).
-//
-// The operation can be descibed as round towards zero, then select the 32 least
-// bits of the resulting value in 2s-complement representation.
-int32_t toInt32(double number)
-{
-    int64_t bits = WTF::bitwise_cast<int64_t>(number);
-    int32_t exp = (static_cast<int32_t>(bits >> 52) & 0x7ff) - 0x3ff;
-
-    // If exponent < 0 there will be no bits to the left of the decimal point
-    // after rounding; if the exponent is > 83 then no bits of precision can be
-    // left in the low 32-bit range of the result (IEEE-754 doubles have 52 bits
-    // of fractional precision).
-    // Note this case handles 0, -0, and all infinte, NaN, & denormal value. 
-    if (exp < 0 || exp > 83)
-        return 0;
-
-    // Select the appropriate 32-bits from the floating point mantissa.  If the
-    // exponent is 52 then the bits we need to select are already aligned to the
-    // lowest bits of the 64-bit integer representation of tghe number, no need
-    // to shift.  If the exponent is greater than 52 we need to shift the value
-    // left by (exp - 52), if the value is less than 52 we need to shift right
-    // accordingly.
-    int32_t result = (exp > 52)
-        ? static_cast<int32_t>(bits << (exp - 52))
-        : static_cast<int32_t>(bits >> (52 - exp));
-
-    // IEEE-754 double precision values are stored omitting an implicit 1 before
-    // the decimal point; we need to reinsert this now.  We may also the shifted
-    // invalid bits into the result that are not a part of the mantissa (the sign
-    // and exponent bits from the floatingpoint representation); mask these out.
-    if (exp < 32) {
-        int32_t missingOne = 1 << exp;
-        result &= missingOne - 1;
-        result += missingOne;
-    }
-
-    // If the input value was negative (we could test either 'number' or 'bits',
-    // but testing 'bits' is likely faster) invert the result appropriately.
-    return bits < 0 ? -result : result;
-}
-
 bool JSValue::isValidCallee()
 {
     return asObject(asCell())->globalObject();
index cfcb35f..7ce3457 100644 (file)
@@ -33,6 +33,7 @@
 #include "JSCellInlines.h"
 #include "JSObject.h"
 #include "JSFunction.h"
+#include "MathCommon.h"
 #include <wtf/text/StringImpl.h>
 
 namespace JSC {
index 7e2f746..6f11975 100644 (file)
@@ -462,6 +462,11 @@ double JIT_OPERATION operationMathPow(double x, double y)
     return mathPowInternal(x, y);
 }
 
+int32_t JIT_OPERATION operationToInt32(double value)
+{
+    return JSC::toInt32(value);
+}
+
 #if HAVE(ARM_IDIV_INSTRUCTIONS)
 static inline bool isStrictInt32(double value)
 {
index 96c67de..8519824 100644 (file)
@@ -39,6 +39,7 @@ namespace JSC {
 
 const int32_t maxExponentForIntegerMathPow = 1000;
 double JIT_OPERATION operationMathPow(double x, double y) WTF_INTERNAL;
+int32_t JIT_OPERATION operationToInt32(double) WTF_INTERNAL;
 
 inline int clz32(uint32_t number)
 {
@@ -59,6 +60,51 @@ inline int clz32(uint32_t number)
 #endif
 }
 
+// This in the ToInt32 operation is defined in section 9.5 of the ECMA-262 spec.
+// Note that this operation is identical to ToUInt32 other than to interpretation
+// of the resulting bit-pattern (as such this metod is also called to implement
+// ToUInt32).
+//
+// The operation can be descibed as round towards zero, then select the 32 least
+// bits of the resulting value in 2s-complement representation.
+ALWAYS_INLINE int32_t toInt32(double number)
+{
+    int64_t bits = WTF::bitwise_cast<int64_t>(number);
+    int32_t exp = (static_cast<int32_t>(bits >> 52) & 0x7ff) - 0x3ff;
+
+    // If exponent < 0 there will be no bits to the left of the decimal point
+    // after rounding; if the exponent is > 83 then no bits of precision can be
+    // left in the low 32-bit range of the result (IEEE-754 doubles have 52 bits
+    // of fractional precision).
+    // Note this case handles 0, -0, and all infinte, NaN, & denormal value.
+    if (exp < 0 || exp > 83)
+        return 0;
+
+    // Select the appropriate 32-bits from the floating point mantissa. If the
+    // exponent is 52 then the bits we need to select are already aligned to the
+    // lowest bits of the 64-bit integer representation of tghe number, no need
+    // to shift. If the exponent is greater than 52 we need to shift the value
+    // left by (exp - 52), if the value is less than 52 we need to shift right
+    // accordingly.
+    int32_t result = (exp > 52)
+        ? static_cast<int32_t>(bits << (exp - 52))
+        : static_cast<int32_t>(bits >> (52 - exp));
+
+    // IEEE-754 double precision values are stored omitting an implicit 1 before
+    // the decimal point; we need to reinsert this now. We may also the shifted
+    // invalid bits into the result that are not a part of the mantissa (the sign
+    // and exponent bits from the floatingpoint representation); mask these out.
+    if (exp < 32) {
+        int32_t missingOne = 1 << exp;
+        result &= missingOne - 1;
+        result += missingOne;
+    }
+
+    // If the input value was negative (we could test either 'number' or 'bits',
+    // but testing 'bits' is likely faster) invert the result appropriately.
+    return bits < 0 ? -result : result;
+}
+
 inline Optional<double> safeReciprocalForDivByConst(double constant)
 {
     // No "weird" numbers (NaN, Denormal, etc).