Title: [147965] trunk
Revision
147965
Author
[email protected]
Date
2013-04-08 17:10:16 -0700 (Mon, 08 Apr 2013)

Log Message

DFG should be able to inline string equality comparisons
https://bugs.webkit.org/show_bug.cgi?id=114224

Source/_javascript_Core: 

Reviewed by Oliver Hunt.
        
Inline 8-bit string equality, go to slow path for 16-bit strings. 2x speed-up for string equality
comparisons on 8-bit strings. 20-50% speed-up on JSRegress/HashMap tests. 30% speed-up on
string-fasta. 2% speed-up on SunSpider overall. Some small speed-ups elsewhere.

This is a gnarly change but we have loads of test coverage already between the HashMap tests and
preexisting DFG string equality tests (which appear to have been designed to test OSR exits, but
also give us good overall coverage on string equality behavior).

* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compilePeepHoleBranch):
(JSC::DFG::SpeculativeJIT::compare):
(JSC::DFG::SpeculativeJIT::compileStrictEq):
(JSC::DFG::SpeculativeJIT::compileStringEquality):
(DFG):
* dfg/DFGSpeculativeJIT.h:
(SpeculativeJIT):

LayoutTests: 

Reviewed by Oliver Hunt.

* fast/js/regress/script-tests/string-equality.js: Added.
(foo):
* fast/js/regress/string-equality-expected.txt: Added.
* fast/js/regress/string-equality.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (147964 => 147965)


--- trunk/LayoutTests/ChangeLog	2013-04-08 23:59:24 UTC (rev 147964)
+++ trunk/LayoutTests/ChangeLog	2013-04-09 00:10:16 UTC (rev 147965)
@@ -1,3 +1,15 @@
+2013-04-08  Filip Pizlo  <[email protected]>
+
+        DFG should be able to inline string equality comparisons
+        https://bugs.webkit.org/show_bug.cgi?id=114224
+
+        Reviewed by Oliver Hunt.
+
+        * fast/js/regress/script-tests/string-equality.js: Added.
+        (foo):
+        * fast/js/regress/string-equality-expected.txt: Added.
+        * fast/js/regress/string-equality.html: Added.
+
 2013-04-08  Benjamin Poulain  <[email protected]>
 
         [Mac][WebKit2] The test images-enabled-unset-can-block-image-and-can-reload-in-place.html passes

Added: trunk/LayoutTests/fast/js/regress/script-tests/string-equality.js (0 => 147965)


--- trunk/LayoutTests/fast/js/regress/script-tests/string-equality.js	                        (rev 0)
+++ trunk/LayoutTests/fast/js/regress/script-tests/string-equality.js	2013-04-09 00:10:16 UTC (rev 147965)
@@ -0,0 +1,16 @@
+var array = [ "a", "b", "c", "d" ];
+
+function foo(array, s) {
+    for (var i = 0; i < array.length; ++i) {
+        if (array[i] == s)
+            return true;
+    }
+    return false;
+}
+
+var result = 0;
+for (var i = 0; i < 1000000; ++i)
+    result += foo(array, "d");
+
+if (result != 1000000)
+    throw "Bad result: " + result;

Added: trunk/LayoutTests/fast/js/regress/string-equality-expected.txt (0 => 147965)


--- trunk/LayoutTests/fast/js/regress/string-equality-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/js/regress/string-equality-expected.txt	2013-04-09 00:10:16 UTC (rev 147965)
@@ -0,0 +1,10 @@
+JSRegress/string-equality
+
+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/fast/js/regress/string-equality.html (0 => 147965)


--- trunk/LayoutTests/fast/js/regress/string-equality.html	                        (rev 0)
+++ trunk/LayoutTests/fast/js/regress/string-equality.html	2013-04-09 00:10:16 UTC (rev 147965)
@@ -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 (147964 => 147965)


--- trunk/Source/_javascript_Core/ChangeLog	2013-04-08 23:59:24 UTC (rev 147964)
+++ trunk/Source/_javascript_Core/ChangeLog	2013-04-09 00:10:16 UTC (rev 147965)
@@ -1,3 +1,31 @@
+2013-04-08  Filip Pizlo  <[email protected]>
+
+        DFG should be able to inline string equality comparisons
+        https://bugs.webkit.org/show_bug.cgi?id=114224
+
+        Reviewed by Oliver Hunt.
+        
+        Inline 8-bit string equality, go to slow path for 16-bit strings. 2x speed-up for string equality
+        comparisons on 8-bit strings. 20-50% speed-up on JSRegress/HashMap tests. 30% speed-up on
+        string-fasta. 2% speed-up on SunSpider overall. Some small speed-ups elsewhere.
+
+        This is a gnarly change but we have loads of test coverage already between the HashMap tests and
+        preexisting DFG string equality tests (which appear to have been designed to test OSR exits, but
+        also give us good overall coverage on string equality behavior).
+
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compilePeepHoleBranch):
+        (JSC::DFG::SpeculativeJIT::compare):
+        (JSC::DFG::SpeculativeJIT::compileStrictEq):
+        (JSC::DFG::SpeculativeJIT::compileStringEquality):
+        (DFG):
+        * dfg/DFGSpeculativeJIT.h:
+        (SpeculativeJIT):
+
 2013-04-08  Geoffrey Garen  <[email protected]>
 
         Stop #include-ing all of _javascript_Core in every DOM-related file

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (147964 => 147965)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2013-04-08 23:59:24 UTC (rev 147964)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2013-04-09 00:10:16 UTC (rev 147965)
@@ -284,14 +284,15 @@
             break;
         }
             
+        case CompareEqConstant: {
+            break;
+        }
+
         case CompareEq:
-        case CompareEqConstant:
         case CompareLess:
         case CompareLessEq:
         case CompareGreater:
         case CompareGreaterEq: {
-            if (node->op() == CompareEqConstant)
-                break;
             if (Node::shouldSpeculateInteger(node->child1().node(), node->child2().node())) {
                 setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
                 setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
@@ -304,8 +305,11 @@
             }
             if (node->op() != CompareEq)
                 break;
-            if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString())
+            if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && GPRInfo::numberOfRegisters >= 7) {
+                setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
+                setUseKindAndUnboxIfProfitable<StringUse>(node->child2());
                 break;
+            }
             if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
                 setUseKindAndUnboxIfProfitable<ObjectUse>(node->child1());
                 setUseKindAndUnboxIfProfitable<ObjectUse>(node->child2());
@@ -324,10 +328,11 @@
             break;
         }
             
-        case CompareStrictEq:
         case CompareStrictEqConstant: {
-            if (node->op() == CompareStrictEqConstant)
-                break;
+            break;
+        }
+            
+        case CompareStrictEq: {
             if (Node::shouldSpeculateInteger(node->child1().node(), node->child2().node())) {
                 setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
                 setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
@@ -338,8 +343,11 @@
                 fixDoubleEdge<NumberUse>(node->child2());
                 break;
             }
-            if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString())
+            if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && GPRInfo::numberOfRegisters >= 7) {
+                setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
+                setUseKindAndUnboxIfProfitable<StringUse>(node->child2());
                 break;
+            }
             if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
                 setUseKindAndUnboxIfProfitable<ObjectUse>(node->child1());
                 setUseKindAndUnboxIfProfitable<ObjectUse>(node->child2());

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (147964 => 147965)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2013-04-08 23:59:24 UTC (rev 147964)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2013-04-09 00:10:16 UTC (rev 147965)
@@ -995,6 +995,23 @@
     return JSValue::equalSlowCaseInline(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
 }
 
+#if USE(JSVALUE64)
+EncodedJSValue DFG_OPERATION operationCompareStringEq(ExecState* exec, JSCell* left, JSCell* right)
+#else
+size_t DFG_OPERATION operationCompareStringEq(ExecState* exec, JSCell* left, JSCell* right)
+#endif
+{
+    JSGlobalData* globalData = &exec->globalData();
+    NativeCallFrameTracer tracer(globalData, exec);
+    
+    bool result = asString(left)->value(exec) == asString(right)->value(exec);
+#if USE(JSVALUE64)
+    return JSValue::encode(jsBoolean(result));
+#else
+    return result;
+#endif
+}
+
 size_t DFG_OPERATION operationCompareStrictEqCell(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
     JSGlobalData* globalData = &exec->globalData();

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (147964 => 147965)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.h	2013-04-08 23:59:24 UTC (rev 147964)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h	2013-04-09 00:10:16 UTC (rev 147965)
@@ -183,6 +183,11 @@
 size_t DFG_OPERATION operationCompareGreater(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 size_t DFG_OPERATION operationCompareGreaterEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 size_t DFG_OPERATION operationCompareEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
+#if USE(JSVALUE64)
+EncodedJSValue DFG_OPERATION operationCompareStringEq(ExecState*, JSCell* left, JSCell* right) WTF_INTERNAL;
+#else
+size_t DFG_OPERATION operationCompareStringEq(ExecState*, JSCell* left, JSCell* right) WTF_INTERNAL;
+#endif
 size_t DFG_OPERATION operationCompareStrictEqCell(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 size_t DFG_OPERATION operationCompareStrictEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 char* DFG_OPERATION operationVirtualCall(ExecState*) WTF_INTERNAL;

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (147964 => 147965)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2013-04-08 23:59:24 UTC (rev 147964)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2013-04-09 00:10:16 UTC (rev 147965)
@@ -1518,8 +1518,8 @@
             compilePeepHoleDoubleBranch(node, branchNode, doubleCondition);
         else if (node->op() == CompareEq) {
             if (node->isBinaryUseKind(StringUse)) {
-                nonSpeculativePeepholeBranch(node, branchNode, condition, operation);
-                return true;
+                // Use non-peephole comparison, for now.
+                return false;
             }
             if (node->isBinaryUseKind(ObjectUse))
                 compilePeepHoleObjectEquality(node, branchNode);
@@ -3476,7 +3476,7 @@
     
     if (node->op() == CompareEq) {
         if (node->isBinaryUseKind(StringUse)) {
-            nonSpeculativeNonPeepholeCompare(node, condition, operation);
+            compileStringEquality(node);
             return false;
         }
         
@@ -3610,7 +3610,8 @@
     }
         
     case StringUse: {
-        return nonSpeculativeStrictEq(node);
+        compileStringEquality(node);
+        return false;
     }
         
     case ObjectUse: {
@@ -3638,6 +3639,111 @@
     }
 }
 
+void SpeculativeJIT::compileStringEquality(Node* node)
+{
+    SpeculateCellOperand left(this, node->child1());
+    SpeculateCellOperand right(this, node->child2());
+    GPRTemporary length(this);
+    GPRTemporary leftTemp(this);
+    GPRTemporary rightTemp(this);
+    GPRTemporary leftTemp2(this, left);
+    GPRTemporary rightTemp2(this, right);
+    
+    GPRReg leftGPR = left.gpr();
+    GPRReg rightGPR = right.gpr();
+    GPRReg lengthGPR = length.gpr();
+    GPRReg leftTempGPR = leftTemp.gpr();
+    GPRReg rightTempGPR = rightTemp.gpr();
+    GPRReg leftTemp2GPR = leftTemp2.gpr();
+    GPRReg rightTemp2GPR = rightTemp2.gpr();
+    
+    JITCompiler::JumpList trueCase;
+    JITCompiler::JumpList falseCase;
+    JITCompiler::JumpList slowCase;
+    
+    DFG_TYPE_CHECK(
+        JSValueSource::unboxedCell(leftGPR), node->child1(), SpecString, m_jit.branchPtr(
+            MacroAssembler::NotEqual,
+            MacroAssembler::Address(leftGPR, JSCell::structureOffset()),
+            MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get())));
+    
+    // It's safe to branch around the type check below, since proving that the values are
+    // equal does indeed prove that the right value is a string.
+    trueCase.append(m_jit.branchPtr(MacroAssembler::Equal, leftGPR, rightGPR));
+    
+    DFG_TYPE_CHECK(
+        JSValueSource::unboxedCell(rightGPR), node->child2(), SpecString, m_jit.branchPtr(
+            MacroAssembler::NotEqual,
+            MacroAssembler::Address(rightGPR, JSCell::structureOffset()),
+            MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get())));
+
+    m_jit.load32(MacroAssembler::Address(leftGPR, JSString::offsetOfLength()), lengthGPR);
+    
+    falseCase.append(m_jit.branch32(
+        MacroAssembler::NotEqual,
+        MacroAssembler::Address(rightGPR, JSString::offsetOfLength()),
+        lengthGPR));
+    
+    trueCase.append(m_jit.branchTest32(MacroAssembler::Zero, lengthGPR));
+    
+    m_jit.loadPtr(MacroAssembler::Address(leftGPR, JSString::offsetOfValue()), leftTempGPR);
+    m_jit.loadPtr(MacroAssembler::Address(rightGPR, JSString::offsetOfValue()), rightTempGPR);
+    
+    slowCase.append(m_jit.branchTestPtr(MacroAssembler::Zero, leftTempGPR));
+    slowCase.append(m_jit.branchTestPtr(MacroAssembler::Zero, rightTempGPR));
+    
+    slowCase.append(m_jit.branchTest32(
+        MacroAssembler::Zero,
+        MacroAssembler::Address(leftTempGPR, StringImpl::flagsOffset()),
+        TrustedImm32(StringImpl::flagIs8Bit())));
+    slowCase.append(m_jit.branchTest32(
+        MacroAssembler::Zero,
+        MacroAssembler::Address(rightTempGPR, StringImpl::flagsOffset()),
+        TrustedImm32(StringImpl::flagIs8Bit())));
+    
+    m_jit.loadPtr(MacroAssembler::Address(leftTempGPR, StringImpl::dataOffset()), leftTempGPR);
+    m_jit.loadPtr(MacroAssembler::Address(rightTempGPR, StringImpl::dataOffset()), rightTempGPR);
+    
+    MacroAssembler::Label loop = m_jit.label();
+    
+    m_jit.sub32(TrustedImm32(1), lengthGPR);
+
+    // This isn't going to generate the best code on x86. But that's OK, it's still better
+    // than not inlining.
+    m_jit.load8(MacroAssembler::BaseIndex(leftTempGPR, lengthGPR, MacroAssembler::TimesOne), leftTemp2GPR);
+    m_jit.load8(MacroAssembler::BaseIndex(rightTempGPR, lengthGPR, MacroAssembler::TimesOne), rightTemp2GPR);
+    falseCase.append(m_jit.branch32(MacroAssembler::NotEqual, leftTemp2GPR, rightTemp2GPR));
+    
+    m_jit.branchTest32(MacroAssembler::NonZero, lengthGPR).linkTo(loop, &m_jit);
+    
+    trueCase.link(&m_jit);
+#if USE(JSVALUE64)
+    m_jit.move(TrustedImm64(ValueTrue), leftTempGPR);
+#else
+    m_jit.move(TrustedImm32(true), leftTempGPR);
+#endif
+    
+    JITCompiler::Jump done = m_jit.jump();
+
+    falseCase.link(&m_jit);
+#if USE(JSVALUE64)
+    m_jit.move(TrustedImm64(ValueFalse), leftTempGPR);
+#else
+    m_jit.move(TrustedImm32(false), leftTempGPR);
+#endif
+    
+    done.link(&m_jit);
+    addSlowPathGenerator(
+        slowPathCall(
+            slowCase, this, operationCompareStringEq, leftTempGPR, leftGPR, rightGPR));
+    
+#if USE(JSVALUE64)
+    jsValueResult(leftTempGPR, node, DataFormatJSBoolean);
+#else
+    booleanResult(leftTempGPR, node);
+#endif
+}
+
 void SpeculativeJIT::compileGetIndexedPropertyStorage(Node* node)
 {
     SpeculateCellOperand base(this, node->child1());

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (147964 => 147965)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2013-04-08 23:59:24 UTC (rev 147964)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2013-04-09 00:10:16 UTC (rev 147965)
@@ -2071,6 +2071,7 @@
     void compileValueAdd(Node*);
     void compileObjectOrOtherLogicalNot(Edge value);
     void compileLogicalNot(Node*);
+    void compileStringEquality(Node*);
     void emitObjectOrOtherBranch(Edge value, BlockIndex taken, BlockIndex notTaken);
     void emitBranch(Node*);
     
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to