Title: [214028] trunk
Revision
214028
Author
utatane....@gmail.com
Date
2017-03-15 21:49:47 -0700 (Wed, 15 Mar 2017)

Log Message

[DFG] ToString operation should have fixup for primitives to say this node does not have side effects
https://bugs.webkit.org/show_bug.cgi?id=169544

Reviewed by Saam Barati.

JSTests:

* microbenchmarks/template-string-array.js: Added.
(test):
* stress/to-string-non-cell-use.js: Added.
(shouldBe):
(shouldThrow):

Source/_javascript_Core:

Our DFG ToString only considers well about String operands. While ToString(non cell operand) does not have
any side effect, it is not modeled well in DFG.

This patch introduces a fixup for ToString with NonCellUse edge. If this edge is set, ToString does not
clobber things (like ToLowerCase, producing String). And ToString(NonCellUse) allows us to perform CSE!

Our microbenchmark shows 32.9% improvement due to dropped GetButterfly and CSE for ToString().

                                    baseline                  patched

    template-string-array       12.6284+-0.2766     ^      9.4998+-0.2295        ^ definitely 1.3293x faster

And SixSpeed template_string.es6 shows 16.68x performance improvement due to LICM onto this non-side-effectful ToString().

                                  baseline                  patched

    template_string.es6     3229.7343+-40.5705    ^    193.6077+-36.3349       ^ definitely 16.6818x faster

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupToStringOrCallStringConstructor):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructorOnCell):
(JSC::DFG::SpeculativeJIT::speculateNotCell):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileToStringOrCallStringConstructor):
(JSC::FTL::DFG::LowerDFGToB3::lowNotCell):
(JSC::FTL::DFG::LowerDFGToB3::speculateNotCell):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (214027 => 214028)


--- trunk/JSTests/ChangeLog	2017-03-16 04:10:53 UTC (rev 214027)
+++ trunk/JSTests/ChangeLog	2017-03-16 04:49:47 UTC (rev 214028)
@@ -1,3 +1,16 @@
+2017-03-15  Yusuke Suzuki  <utatane....@gmail.com>
+
+        [DFG] ToString operation should have fixup for primitives to say this node does not have side effects
+        https://bugs.webkit.org/show_bug.cgi?id=169544
+
+        Reviewed by Saam Barati.
+
+        * microbenchmarks/template-string-array.js: Added.
+        (test):
+        * stress/to-string-non-cell-use.js: Added.
+        (shouldBe):
+        (shouldThrow):
+
 2017-03-13  Commit Queue  <commit-qu...@webkit.org>
 
         Unreviewed, rolling out r213856.

Added: trunk/JSTests/microbenchmarks/template-string-array.js (0 => 214028)


--- trunk/JSTests/microbenchmarks/template-string-array.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/template-string-array.js	2017-03-16 04:49:47 UTC (rev 214028)
@@ -0,0 +1,9 @@
+var array = [1, 2, 3];
+function test()
+{
+    return `${array[0]}, ${array[1]}, ${array[2]}, ${array[0]}, ${array[1]}, ${array[2]}`;
+}
+noInline(test);
+
+for (var i = 0; i < 1e5; ++i)
+    test();

Added: trunk/JSTests/stress/to-string-non-cell-use.js (0 => 214028)


--- trunk/JSTests/stress/to-string-non-cell-use.js	                        (rev 0)
+++ trunk/JSTests/stress/to-string-non-cell-use.js	2017-03-16 04:49:47 UTC (rev 214028)
@@ -0,0 +1,43 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage)
+{
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+function toString(value)
+{
+    return `${value}`;
+}
+noInline(toString);
+
+for (var i = 0; i < 1e4; ++i) {
+    shouldBe(toString(i), i + "");
+    shouldBe(toString(null), "null");
+    shouldBe(toString(undefined), "undefined");
+    shouldBe(toString(10.5), "10.5");
+    shouldBe(toString(-10.5), "-10.5");
+    shouldBe(toString(true), "true");
+    shouldBe(toString(false), "false");
+    shouldBe(toString(0 / 0), "NaN");
+}
+
+shouldBe(toString("HELLO"), "HELLO");
+shouldThrow(() => {
+    toString(Symbol("Cocoa"));
+}, `TypeError: Cannot convert a symbol to a string`);

Modified: trunk/Source/_javascript_Core/ChangeLog (214027 => 214028)


--- trunk/Source/_javascript_Core/ChangeLog	2017-03-16 04:10:53 UTC (rev 214027)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-03-16 04:49:47 UTC (rev 214028)
@@ -1,3 +1,47 @@
+2017-03-15  Yusuke Suzuki  <utatane....@gmail.com>
+
+        [DFG] ToString operation should have fixup for primitives to say this node does not have side effects
+        https://bugs.webkit.org/show_bug.cgi?id=169544
+
+        Reviewed by Saam Barati.
+
+        Our DFG ToString only considers well about String operands. While ToString(non cell operand) does not have
+        any side effect, it is not modeled well in DFG.
+
+        This patch introduces a fixup for ToString with NonCellUse edge. If this edge is set, ToString does not
+        clobber things (like ToLowerCase, producing String). And ToString(NonCellUse) allows us to perform CSE!
+
+        Our microbenchmark shows 32.9% improvement due to dropped GetButterfly and CSE for ToString().
+
+                                            baseline                  patched
+
+            template-string-array       12.6284+-0.2766     ^      9.4998+-0.2295        ^ definitely 1.3293x faster
+
+        And SixSpeed template_string.es6 shows 16.68x performance improvement due to LICM onto this non-side-effectful ToString().
+
+                                          baseline                  patched
+
+            template_string.es6     3229.7343+-40.5705    ^    193.6077+-36.3349       ^ definitely 16.6818x faster
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupToStringOrCallStringConstructor):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructorOnCell):
+        (JSC::DFG::SpeculativeJIT::speculateNotCell):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileToStringOrCallStringConstructor):
+        (JSC::FTL::DFG::LowerDFGToB3::lowNotCell):
+        (JSC::FTL::DFG::LowerDFGToB3::speculateNotCell):
+
 2017-03-15  Ryan Haddad  <ryanhad...@apple.com>
 
         Revert part of r213978 to see if it resolves LayoutTest crashes.

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (214027 => 214028)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2017-03-16 04:10:53 UTC (rev 214027)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2017-03-16 04:49:47 UTC (rev 214028)
@@ -1858,6 +1858,7 @@
                 m_graph.registerStructure(m_graph.globalObjectFor(node->origin.semantic)->stringObjectStructure()));
             break;
         case StringOrStringObjectUse:
+        case NotCellUse:
             break;
         case CellUse:
         case UntypedUse:

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (214027 => 214028)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2017-03-16 04:10:53 UTC (rev 214027)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2017-03-16 04:49:47 UTC (rev 214028)
@@ -1408,6 +1408,10 @@
             read(World);
             write(Heap);
             return;
+
+        case NotCellUse:
+            def(PureValue(node));
+            return;
             
         default:
             RELEASE_ASSERT_NOT_REACHED();

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (214027 => 214028)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2017-03-16 04:10:53 UTC (rev 214027)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2017-03-16 04:49:47 UTC (rev 214028)
@@ -2235,6 +2235,16 @@
             fixEdge<CellUse>(node->child1());
             return;
         }
+
+        // ToString(Symbol) throws an error. So if the child1 can include Symbols,
+        // we need to care about it in the clobberize. In the following case,
+        // since NotCellUse edge filter is used and this edge filters Symbols,
+        // we can say that ToString never throws an error!
+        if (node->child1()->shouldSpeculateNotCell()) {
+            fixEdge<NotCellUse>(node->child1());
+            node->clearFlags(NodeMustGenerate);
+            return;
+        }
     }
 
     bool attemptToMakeFastStringAdd(Node* node)

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (214027 => 214028)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2017-03-16 04:10:53 UTC (rev 214027)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2017-03-16 04:49:47 UTC (rev 214028)
@@ -7925,6 +7925,61 @@
 
 void SpeculativeJIT::compileToStringOrCallStringConstructorOnCell(Node* node)
 {
+    if (node->child1().useKind() == NotCellUse) {
+        JSValueOperand op1(this, node->child1(), ManualOperandSpeculation);
+        JSValueRegs op1Regs = op1.jsValueRegs();
+
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+
+        speculateNotCell(node->child1(), op1Regs);
+
+        flushRegisters();
+
+        if (node->op() == ToString)
+            callOperation(operationToString, resultGPR, op1Regs);
+        else {
+            ASSERT(node->op() == CallStringConstructor);
+            callOperation(operationCallStringConstructor, resultGPR, op1Regs);
+        }
+        m_jit.exceptionCheck();
+        cellResult(resultGPR, node);
+        return;
+    }
+
+    if (node->child1().useKind() == UntypedUse) {
+        JSValueOperand op1(this, node->child1());
+        JSValueRegs op1Regs = op1.jsValueRegs();
+        GPRReg op1PayloadGPR = op1Regs.payloadGPR();
+
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+
+        flushRegisters();
+
+        JITCompiler::Jump done;
+        if (node->child1()->prediction() & SpecString) {
+            JITCompiler::Jump slowPath1 = m_jit.branchIfNotCell(op1.jsValueRegs());
+            JITCompiler::Jump slowPath2 = m_jit.branchIfNotString(op1PayloadGPR);
+            m_jit.move(op1PayloadGPR, resultGPR);
+            done = m_jit.jump();
+            slowPath1.link(&m_jit);
+            slowPath2.link(&m_jit);
+        }
+        if (node->op() == ToString)
+            callOperation(operationToString, resultGPR, op1Regs);
+        else {
+            ASSERT(node->op() == CallStringConstructor);
+            callOperation(operationCallStringConstructor, resultGPR, op1Regs);
+        }
+        m_jit.exceptionCheck();
+        if (done.isSet())
+            done.link(&m_jit);
+        cellResult(resultGPR, node);
+        return;
+    }
+
+
     SpeculateCellOperand op1(this, node->child1());
     GPRReg op1GPR = op1.gpr();
     
@@ -8533,6 +8588,11 @@
     speculateSymbol(edge, operand.gpr());
 }
 
+void SpeculativeJIT::speculateNotCell(Edge edge, JSValueRegs regs)
+{
+    DFG_TYPE_CHECK(regs, edge, ~SpecCell, m_jit.branchIfCell(regs));
+}
+
 void SpeculativeJIT::speculateNotCell(Edge edge)
 {
     if (!needsTypeCheck(edge, ~SpecCell))
@@ -8539,7 +8599,7 @@
         return;
     
     JSValueOperand operand(this, edge, ManualOperandSpeculation); 
-    typeCheck(operand.jsValueRegs(), edge, ~SpecCell, m_jit.branchIfCell(operand.jsValueRegs()));
+    speculateNotCell(edge, operand.jsValueRegs());
 }
 
 void SpeculativeJIT::speculateOther(Edge edge)

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (214027 => 214028)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2017-03-16 04:10:53 UTC (rev 214027)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2017-03-16 04:49:47 UTC (rev 214028)
@@ -1644,6 +1644,11 @@
         m_jit.setupArgumentsWithExecState(arg1);
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(C_JITOperation_EJ operation, GPRReg result, JSValueRegs arg1)
+    {
+        m_jit.setupArgumentsWithExecState(arg1.gpr());
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(C_JITOperation_EJJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -2954,6 +2959,7 @@
     void speculateStringOrStringObject(Edge);
     void speculateSymbol(Edge, GPRReg cell);
     void speculateSymbol(Edge);
+    void speculateNotCell(Edge, JSValueRegs);
     void speculateNotCell(Edge);
     void speculateOther(Edge);
     void speculateMisc(Edge, JSValueRegs);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (214027 => 214028)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2017-03-16 04:10:53 UTC (rev 214027)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2017-03-16 04:49:47 UTC (rev 214028)
@@ -3769,38 +3769,6 @@
         
     case ToString:
     case CallStringConstructor: {
-        if (node->child1().useKind() == UntypedUse) {
-            JSValueOperand op1(this, node->child1());
-            GPRReg op1PayloadGPR = op1.payloadGPR();
-            JSValueRegs op1Regs = op1.jsValueRegs();
-            
-            GPRFlushedCallResult result(this);
-            GPRReg resultGPR = result.gpr();
-            
-            flushRegisters();
-            
-            JITCompiler::Jump done;
-            if (node->child1()->prediction() & SpecString) {
-                JITCompiler::Jump slowPath1 = m_jit.branchIfNotCell(op1.jsValueRegs());
-                JITCompiler::Jump slowPath2 = m_jit.branchIfNotString(op1PayloadGPR);
-                m_jit.move(op1PayloadGPR, resultGPR);
-                done = m_jit.jump();
-                slowPath1.link(&m_jit);
-                slowPath2.link(&m_jit);
-            }
-            if (op == ToString)
-                callOperation(operationToString, resultGPR, op1Regs);
-            else {
-                ASSERT(op == CallStringConstructor);
-                callOperation(operationCallStringConstructor, resultGPR, op1Regs);
-            }
-            m_jit.exceptionCheck();
-            if (done.isSet())
-                done.link(&m_jit);
-            cellResult(resultGPR, node);
-            break;
-        }
-        
         compileToStringOrCallStringConstructorOnCell(node);
         break;
     }

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (214027 => 214028)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2017-03-16 04:10:53 UTC (rev 214027)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2017-03-16 04:49:47 UTC (rev 214028)
@@ -3739,37 +3739,6 @@
         
     case ToString:
     case CallStringConstructor: {
-        if (node->child1().useKind() == UntypedUse) {
-            JSValueOperand op1(this, node->child1());
-            GPRReg op1GPR = op1.gpr();
-            
-            GPRFlushedCallResult result(this);
-            GPRReg resultGPR = result.gpr();
-            
-            flushRegisters();
-            
-            JITCompiler::Jump done;
-            if (node->child1()->prediction() & SpecString) {
-                JITCompiler::Jump slowPath1 = m_jit.branchIfNotCell(JSValueRegs(op1GPR));
-                JITCompiler::Jump slowPath2 = m_jit.branchIfNotString(op1GPR);
-                m_jit.move(op1GPR, resultGPR);
-                done = m_jit.jump();
-                slowPath1.link(&m_jit);
-                slowPath2.link(&m_jit);
-            }
-            if (op == ToString)
-                callOperation(operationToString, resultGPR, op1GPR);
-            else {
-                ASSERT(op == CallStringConstructor);
-                callOperation(operationCallStringConstructor, resultGPR, op1GPR);
-            }
-            m_jit.exceptionCheck();
-            if (done.isSet())
-                done.link(&m_jit);
-            cellResult(resultGPR, node);
-            break;
-        }
-        
         compileToStringOrCallStringConstructorOnCell(node);
         break;
     }

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (214027 => 214028)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2017-03-16 04:10:53 UTC (rev 214027)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2017-03-16 04:49:47 UTC (rev 214028)
@@ -4933,10 +4933,13 @@
         }
             
         case CellUse:
+        case NotCellUse:
         case UntypedUse: {
             LValue value;
             if (m_node->child1().useKind() == CellUse)
                 value = lowCell(m_node->child1());
+            else if (m_node->child1().useKind() == NotCellUse)
+                value = lowNotCell(m_node->child1());
             else
                 value = lowJSValue(m_node->child1());
             
@@ -4947,6 +4950,8 @@
             LValue isCellPredicate;
             if (m_node->child1().useKind() == CellUse)
                 isCellPredicate = m_out.booleanTrue;
+            else if (m_node->child1().useKind() == NotCellUse)
+                isCellPredicate = m_out.booleanFalse;
             else
                 isCellPredicate = this->isCell(value, provenType(m_node->child1()));
             m_out.branch(isCellPredicate, unsure(isCell), unsure(notString));
@@ -12228,6 +12233,13 @@
         DFG_CRASH(m_graph, m_node, "Value not defined");
         return 0;
     }
+
+    LValue lowNotCell(Edge edge)
+    {
+        LValue result = lowJSValue(edge, ManualOperandSpeculation);
+        FTL_TYPE_CHECK(jsValueValue(result), edge, ~SpecCell, isCell(result));
+        return result;
+    }
     
     LValue lowStorage(Edge edge)
     {
@@ -12632,6 +12644,13 @@
     {
         lowCell(edge);
     }
+
+    void speculateNotCell(Edge edge)
+    {
+        if (!m_interpreter.needsTypeCheck(edge))
+            return;
+        lowNotCell(edge);
+    }
     
     void speculateCellOrOther(Edge edge)
     {
@@ -13150,15 +13169,6 @@
         m_out.appendTo(continuation, lastNext);
     }
     
-    void speculateNotCell(Edge edge)
-    {
-        if (!m_interpreter.needsTypeCheck(edge))
-            return;
-        
-        LValue value = lowJSValue(edge, ManualOperandSpeculation);
-        typeCheck(jsValueValue(value), edge, ~SpecCell, isCell(value));
-    }
-    
     void speculateOther(Edge edge)
     {
         if (!m_interpreter.needsTypeCheck(edge))
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to