Title: [224276] trunk
Revision
224276
Author
[email protected]
Date
2017-11-01 06:25:21 -0700 (Wed, 01 Nov 2017)

Log Message

[DFG][FTL] Introduce StringSlice
https://bugs.webkit.org/show_bug.cgi?id=178934

Reviewed by Saam Barati.

JSTests:

* microbenchmarks/string-slice-empty.js: Added.
(slice):
* microbenchmarks/string-slice-one-char.js: Added.
(slice):
* microbenchmarks/string-slice.js: Added.
(slice):

Source/_javascript_Core:

String.prototype.slice is one of the most frequently called function in ARES-6/Babylon.
This patch introduces StringSlice DFG node to optimize it in DFG and FTL.

This patch's StringSlice node optimizes the following things.

1. Empty string generation is accelerated. It is fully executed inline.
2. One char string generation is accelerated. `< 0x100` character is supported right now.
It is the same to charAt acceleration.
3. We calculate start and end index in DFG/FTL with Int32Use information and call optimized
operation.

We do not inline (3)'s operation right now since we do not have a way to call bmalloc allocation from DFG / FTL.
And we do not optimize String.prototype.{substring,substr} right now. But they can be optimized based on this change
in subsequent changes.

This patch improves ARES-6/Babylon performance by 3% in steady state.

Baseline:
    Running... Babylon ( 1  to go)
    firstIteration:     50.05 +- 13.68 ms
    averageWorstCase:   16.80 +- 1.27 ms
    steadyState:        7.53 +- 0.22 ms

Patched:
    Running... Babylon ( 1  to go)
    firstIteration:     50.91 +- 13.41 ms
    averageWorstCase:   16.12 +- 0.99 ms
    steadyState:        7.30 +- 0.29 ms

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileStringSlice):
(JSC::DFG::SpeculativeJIT::emitPopulateSliceIndex):
(JSC::DFG::SpeculativeJIT::compileArraySlice):
(JSC::DFG::SpeculativeJIT::compileArrayIndexOf):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::populateSliceRange):
(JSC::FTL::DFG::LowerDFGToB3::compileArraySlice):
(JSC::FTL::DFG::LowerDFGToB3::compileStringSlice):
* jit/JITOperations.h:
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/StringPrototype.cpp:
(JSC::StringPrototype::finishCreation):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (224275 => 224276)


--- trunk/JSTests/ChangeLog	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/JSTests/ChangeLog	2017-11-01 13:25:21 UTC (rev 224276)
@@ -1,3 +1,17 @@
+2017-10-27  Yusuke Suzuki  <[email protected]>
+
+        [DFG][FTL] Introduce StringSlice
+        https://bugs.webkit.org/show_bug.cgi?id=178934
+
+        Reviewed by Saam Barati.
+
+        * microbenchmarks/string-slice-empty.js: Added.
+        (slice):
+        * microbenchmarks/string-slice-one-char.js: Added.
+        (slice):
+        * microbenchmarks/string-slice.js: Added.
+        (slice):
+
 2017-10-26  Michael Saboff  <[email protected]>
 
         REGRESSION(r222601): We fail to properly backtrack into a sub pattern of a parenthesis with non-zero minimum

Added: trunk/JSTests/microbenchmarks/string-slice-empty.js (0 => 224276)


--- trunk/JSTests/microbenchmarks/string-slice-empty.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/string-slice-empty.js	2017-11-01 13:25:21 UTC (rev 224276)
@@ -0,0 +1,8 @@
+function slice(string, start, end)
+{
+    return string.slice(start, end);
+}
+noInline(slice);
+
+for (var i = 0; i < 1e6; ++i)
+    slice("Cocoa", 3, 3);

Added: trunk/JSTests/microbenchmarks/string-slice-one-char.js (0 => 224276)


--- trunk/JSTests/microbenchmarks/string-slice-one-char.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/string-slice-one-char.js	2017-11-01 13:25:21 UTC (rev 224276)
@@ -0,0 +1,8 @@
+function slice(string, start, end)
+{
+    return string.slice(start, end);
+}
+noInline(slice);
+
+for (var i = 0; i < 1e6; ++i)
+    slice("Cocoa", 2, 3);

Added: trunk/JSTests/microbenchmarks/string-slice.js (0 => 224276)


--- trunk/JSTests/microbenchmarks/string-slice.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/string-slice.js	2017-11-01 13:25:21 UTC (rev 224276)
@@ -0,0 +1,8 @@
+function slice(string, start, end)
+{
+    return string.slice(start, end);
+}
+noInline(slice);
+
+for (var i = 0; i < 1e6; ++i)
+    slice("Cocoa", 2, 4);

Modified: trunk/Source/_javascript_Core/ChangeLog (224275 => 224276)


--- trunk/Source/_javascript_Core/ChangeLog	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-11-01 13:25:21 UTC (rev 224276)
@@ -1,3 +1,82 @@
+2017-10-27  Yusuke Suzuki  <[email protected]>
+
+        [DFG][FTL] Introduce StringSlice
+        https://bugs.webkit.org/show_bug.cgi?id=178934
+
+        Reviewed by Saam Barati.
+
+        String.prototype.slice is one of the most frequently called function in ARES-6/Babylon.
+        This patch introduces StringSlice DFG node to optimize it in DFG and FTL.
+
+        This patch's StringSlice node optimizes the following things.
+
+        1. Empty string generation is accelerated. It is fully executed inline.
+        2. One char string generation is accelerated. `< 0x100` character is supported right now.
+        It is the same to charAt acceleration.
+        3. We calculate start and end index in DFG/FTL with Int32Use information and call optimized
+        operation.
+
+        We do not inline (3)'s operation right now since we do not have a way to call bmalloc allocation from DFG / FTL.
+        And we do not optimize String.prototype.{substring,substr} right now. But they can be optimized based on this change
+        in subsequent changes.
+
+        This patch improves ARES-6/Babylon performance by 3% in steady state.
+
+        Baseline:
+            Running... Babylon ( 1  to go)
+            firstIteration:     50.05 +- 13.68 ms
+            averageWorstCase:   16.80 +- 1.27 ms
+            steadyState:        7.53 +- 0.22 ms
+
+        Patched:
+            Running... Babylon ( 1  to go)
+            firstIteration:     50.91 +- 13.41 ms
+            averageWorstCase:   16.12 +- 0.99 ms
+            steadyState:        7.30 +- 0.29 ms
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGBackwardsPropagationPhase.cpp:
+        (JSC::DFG::BackwardsPropagationPhase::propagate):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileStringSlice):
+        (JSC::DFG::SpeculativeJIT::emitPopulateSliceIndex):
+        (JSC::DFG::SpeculativeJIT::compileArraySlice):
+        (JSC::DFG::SpeculativeJIT::compileArrayIndexOf):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::populateSliceRange):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArraySlice):
+        (JSC::FTL::DFG::LowerDFGToB3::compileStringSlice):
+        * jit/JITOperations.h:
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/StringPrototype.cpp:
+        (JSC::StringPrototype::finishCreation):
+
 2017-10-31  JF Bastien  <[email protected]>
 
         WebAssembly: Wasm::IndexOrName has a raw pointer to Name

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2017-11-01 13:25:21 UTC (rev 224276)
@@ -1099,6 +1099,11 @@
         break;
     }
 
+    case StringSlice: {
+        forNode(node).setType(m_graph, SpecString);
+        break;
+    }
+
     case ToLowerCase: {
         forNode(node).setType(m_graph, SpecString);
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -226,6 +226,27 @@
             node->child2()->mergeFlags(NodeBytecodeUsesAsValue | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex);
             break;
         }
+
+        case StringSlice: {
+            node->child1()->mergeFlags(NodeBytecodeUsesAsValue);
+            node->child2()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex);
+            if (node->child3())
+                node->child3()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex);
+            break;
+        }
+
+        case ArraySlice: {
+            m_graph.varArgChild(node, 0)->mergeFlags(NodeBytecodeUsesAsValue);
+            m_graph.varArgChild(node, 1)->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex);
+            if (node->numChildren() == 3)
+                m_graph.varArgChild(node, 2)->mergeFlags(NodeBytecodeUsesAsValue);
+            else {
+                m_graph.varArgChild(node, 2)->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex);
+                m_graph.varArgChild(node, 3)->mergeFlags(NodeBytecodeUsesAsValue);
+            }
+            break;
+        }
+
             
         case UInt32ToNumber: {
             node->child1()->mergeFlags(flags);

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -2905,6 +2905,24 @@
         return true;
     }
 
+    case StringPrototypeSliceIntrinsic: {
+        if (argumentCountIncludingThis < 2)
+            return false;
+
+        if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
+            return false;
+
+        insertChecks();
+        Node* thisString = get(virtualRegisterForArgument(0, registerOffset));
+        Node* start = get(virtualRegisterForArgument(1, registerOffset));
+        Node* end = nullptr;
+        if (argumentCountIncludingThis > 2)
+            end = get(virtualRegisterForArgument(2, registerOffset));
+        Node* result = addToGraph(StringSlice, thisString, start, end);
+        set(VirtualRegister(resultOperand), result);
+        return true;
+    }
+
     case StringPrototypeToLowerCaseIntrinsic: {
         if (argumentCountIncludingThis != 1)
             return false;

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2017-11-01 13:25:21 UTC (rev 224276)
@@ -1621,6 +1621,10 @@
         return;
     }
 
+    case StringSlice:
+        def(PureValue(node));
+        return;
+
     case ToLowerCase:
         def(PureValue(node));
         return;

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -332,6 +332,7 @@
     case StrCat:
     case StringReplace:
     case StringReplaceRegExp:
+    case StringSlice:
     case CreateRest:
     case ToLowerCase:
     case CallDOMGetter:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -1956,6 +1956,14 @@
             break;
         }
 
+        case StringSlice: {
+            fixEdge<StringUse>(node->child1());
+            fixEdge<Int32Use>(node->child2());
+            if (node->child3())
+                fixEdge<Int32Use>(node->child3());
+            break;
+        }
+
         case ToLowerCase: {
             // We currently only support StringUse since that will ensure that
             // ToLowerCase is a pure operation. If we decide to update this with

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2017-11-01 13:25:21 UTC (rev 224276)
@@ -444,6 +444,7 @@
     /* Nodes for JSWeakMap and JSWeakSet */ \
     macro(WeakMapGet, NodeResultJS) \
     \
+    macro(StringSlice, NodeResultJS) \
     macro(ToLowerCase, NodeResultJS) \
     /* Nodes for DOM JIT */\
     macro(CallDOMGetter, NodeResultJS | NodeMustGenerate) \

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -1796,6 +1796,17 @@
     return string->value(exec).impl();
 }
 
+JSCell* JIT_OPERATION operationStringSubstr(ExecState* exec, JSCell* cell, int32_t from, int32_t span)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto string = jsCast<JSString*>(cell)->value(exec);
+    RETURN_IF_EXCEPTION(scope, nullptr);
+    return jsSubstring(exec, string, from, span);
+}
+
 JSString* JIT_OPERATION operationToLowerCase(ExecState* exec, JSString* string, uint32_t failingIndex)
 {
     VM& vm = exec->vm();

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.h	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h	2017-11-01 13:25:21 UTC (rev 224276)
@@ -176,6 +176,7 @@
 StringImpl* JIT_OPERATION operationResolveRope(ExecState*, JSString*);
 JSString* JIT_OPERATION operationSingleCharacterString(ExecState*, int32_t);
 
+JSCell* JIT_OPERATION operationStringSubstr(ExecState*, JSCell*, int32_t, int32_t);
 JSString* JIT_OPERATION operationToLowerCase(ExecState*, JSString*, uint32_t);
 
 char* JIT_OPERATION operationInt32ToString(ExecState*, int32_t, int32_t);

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -784,6 +784,7 @@
             break;
         }
 
+        case StringSlice:
         case ToLowerCase:
             setPrediction(SpecString);
             break;

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2017-11-01 13:25:21 UTC (rev 224276)
@@ -412,6 +412,7 @@
     case ResolveScopeForHoistingFuncDeclInEval:
     case ResolveScope:
     case MapHash:
+    case StringSlice:
     case ToLowerCase:
     case GetMapBucket:
     case GetMapBucketHead:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -1516,6 +1516,77 @@
     jump(notTaken);
 }
 
+void SpeculativeJIT::compileStringSlice(Node* node)
+{
+    SpeculateCellOperand string(this, node->child1());
+    GPRTemporary startIndex(this);
+    GPRTemporary temp(this);
+    GPRTemporary temp2(this);
+
+    GPRReg stringGPR = string.gpr();
+    GPRReg startIndexGPR = startIndex.gpr();
+    GPRReg tempGPR = temp.gpr();
+    GPRReg temp2GPR = temp2.gpr();
+
+    speculateString(node->child1(), stringGPR);
+
+    {
+        m_jit.load32(JITCompiler::Address(stringGPR, JSString::offsetOfLength()), temp2GPR);
+
+        emitPopulateSliceIndex(node->child2(), temp2GPR, startIndexGPR);
+        if (node->child3())
+            emitPopulateSliceIndex(node->child3(), temp2GPR, tempGPR);
+        else
+            m_jit.move(temp2GPR, tempGPR);
+    }
+
+    CCallHelpers::JumpList doneCases;
+    CCallHelpers::JumpList slowCases;
+
+    auto nonEmptyCase = m_jit.branch32(MacroAssembler::Below, startIndexGPR, tempGPR);
+    m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(&vm())), tempGPR);
+    doneCases.append(m_jit.jump());
+
+    nonEmptyCase.link(&m_jit);
+    m_jit.sub32(startIndexGPR, tempGPR); // the size of the sliced string.
+    slowCases.append(m_jit.branch32(MacroAssembler::NotEqual, tempGPR, TrustedImm32(1)));
+
+    m_jit.loadPtr(MacroAssembler::Address(stringGPR, JSString::offsetOfValue()), temp2GPR);
+    slowCases.append(m_jit.branchTestPtr(MacroAssembler::Zero, temp2GPR));
+
+    m_jit.loadPtr(MacroAssembler::Address(temp2GPR, StringImpl::dataOffset()), tempGPR);
+
+    // Load the character into scratchReg
+    m_jit.zeroExtend32ToPtr(startIndexGPR, startIndexGPR);
+    auto is16Bit = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(temp2GPR, StringImpl::flagsOffset()), TrustedImm32(StringImpl::flagIs8Bit()));
+
+    m_jit.load8(MacroAssembler::BaseIndex(tempGPR, startIndexGPR, MacroAssembler::TimesOne, 0), tempGPR);
+    auto cont8Bit = m_jit.jump();
+
+    is16Bit.link(&m_jit);
+    m_jit.load16(MacroAssembler::BaseIndex(tempGPR, startIndexGPR, MacroAssembler::TimesTwo, 0), tempGPR);
+
+    auto bigCharacter = m_jit.branch32(MacroAssembler::AboveOrEqual, tempGPR, TrustedImm32(0x100));
+
+    // 8 bit string values don't need the isASCII check.
+    cont8Bit.link(&m_jit);
+
+    m_jit.lshift32(MacroAssembler::TrustedImm32(sizeof(void*) == 4 ? 2 : 3), tempGPR);
+    m_jit.addPtr(TrustedImmPtr(m_jit.vm()->smallStrings.singleCharacterStrings()), tempGPR);
+    m_jit.loadPtr(tempGPR, tempGPR);
+
+    addSlowPathGenerator(
+        slowPathCall(
+            bigCharacter, this, operationSingleCharacterString, tempGPR, tempGPR));
+
+    addSlowPathGenerator(
+        slowPathCall(
+            slowCases, this, operationStringSubstr, tempGPR, stringGPR, startIndexGPR, tempGPR));
+
+    doneCases.link(&m_jit);
+    cellResult(tempGPR, node);
+}
+
 void SpeculativeJIT::compileToLowerCase(Node* node)
 {
     ASSERT(node->op() == ToLowerCase);
@@ -7493,6 +7564,25 @@
     int32Result(resultGPR, node);
 }
 
+void SpeculativeJIT::emitPopulateSliceIndex(Edge& target, GPRReg length, GPRReg result)
+{
+    SpeculateInt32Operand index(this, target);
+    GPRReg indexGPR = index.gpr();
+    MacroAssembler::JumpList done;
+    auto isPositive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, indexGPR, TrustedImm32(0));
+    m_jit.move(length, result);
+    done.append(m_jit.branchAdd32(MacroAssembler::PositiveOrZero, indexGPR, result));
+    m_jit.move(TrustedImm32(0), result);
+    done.append(m_jit.jump());
+
+    isPositive.link(&m_jit);
+    m_jit.move(indexGPR, result);
+    done.append(m_jit.branch32(MacroAssembler::BelowOrEqual, result, length));
+    m_jit.move(length, result);
+
+    done.link(&m_jit);
+}
+
 void SpeculativeJIT::compileArraySlice(Node* node)
 {
     ASSERT(node->op() == ArraySlice);
@@ -7507,24 +7597,6 @@
     GPRReg resultGPR = result.gpr();
     GPRReg tempGPR = temp.gpr();
 
-    auto populateIndex = [&] (unsigned childIndex, GPRReg length, GPRReg result) {
-        SpeculateInt32Operand index(this, m_jit.graph().varArgChild(node, childIndex));
-        GPRReg indexGPR = index.gpr();
-        MacroAssembler::JumpList done;
-        auto isPositive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, indexGPR, TrustedImm32(0));
-        m_jit.move(length, result);
-        done.append(m_jit.branchAdd32(MacroAssembler::PositiveOrZero, indexGPR, result));
-        m_jit.move(TrustedImm32(0), result);
-        done.append(m_jit.jump());
-
-        isPositive.link(&m_jit);
-        m_jit.move(indexGPR, result);
-        done.append(m_jit.branch32(MacroAssembler::BelowOrEqual, result, length));
-        m_jit.move(length, result);
-
-        done.link(&m_jit);
-    };
-
     {
         GPRTemporary tempLength(this);
         GPRReg lengthGPR = tempLength.gpr();
@@ -7531,13 +7603,13 @@
         m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), lengthGPR);
 
         if (node->numChildren() == 4)
-            populateIndex(2, lengthGPR, tempGPR);
+            emitPopulateSliceIndex(m_jit.graph().varArgChild(node, 2), lengthGPR, tempGPR);
         else
             m_jit.move(lengthGPR, tempGPR);
 
         GPRTemporary tempStartIndex(this);
         GPRReg startGPR = tempStartIndex.gpr();
-        populateIndex(1, lengthGPR, startGPR);
+        emitPopulateSliceIndex(m_jit.graph().varArgChild(node, 1), lengthGPR, startGPR);
 
         auto tooBig = m_jit.branch32(MacroAssembler::Above, startGPR, tempGPR);
         m_jit.sub32(startGPR, tempGPR); // the size of the array we'll make.
@@ -7628,10 +7700,10 @@
 
     m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), tempValue);
     if (node->numChildren() == 4)
-        populateIndex(2, tempValue, tempGPR);
+        emitPopulateSliceIndex(m_jit.graph().varArgChild(node, 2), tempValue, tempGPR);
     else
         m_jit.move(tempValue, tempGPR);
-    populateIndex(1, tempValue, loadIndex);
+    emitPopulateSliceIndex(m_jit.graph().varArgChild(node, 1), tempValue, loadIndex);
 
     GPRTemporary temp5(this);
     GPRReg storeIndex = temp5.gpr();
@@ -7684,25 +7756,11 @@
 
     m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), lengthGPR);
 
-    if (node->numChildren() == 4) {
-        SpeculateInt32Operand startIndex(this, m_jit.graph().varArgChild(node, 2));
-        GPRReg startIndexGPR = startIndex.gpr();
-        MacroAssembler::JumpList done;
-        auto isPositive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, startIndexGPR, TrustedImm32(0));
-        m_jit.move(lengthGPR, indexGPR);
-        done.append(m_jit.branchAdd32(MacroAssembler::PositiveOrZero, startIndexGPR, indexGPR));
+    if (node->numChildren() == 4)
+        emitPopulateSliceIndex(m_jit.graph().varArgChild(node, 2), lengthGPR, indexGPR);
+    else
         m_jit.move(TrustedImm32(0), indexGPR);
-        done.append(m_jit.jump());
 
-        isPositive.link(&m_jit);
-        m_jit.move(startIndexGPR, indexGPR);
-        done.append(m_jit.branch32(MacroAssembler::BelowOrEqual, indexGPR, lengthGPR));
-        m_jit.move(lengthGPR, indexGPR);
-
-        done.link(&m_jit);
-    } else
-        m_jit.move(TrustedImm32(0), indexGPR);
-
     Edge& searchElementEdge = m_jit.graph().varArgChild(node, 1);
     switch (searchElementEdge.useKind()) {
     case Int32Use:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2017-11-01 13:25:21 UTC (rev 224276)
@@ -1131,6 +1131,11 @@
         m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure), TrustedImm32(knownLength));
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(C_JITOperation_ECZZ operation, GPRReg result, GPRReg arg1, GPRReg arg2, GPRReg arg3)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(C_JITOperation_EStZZ operation, GPRReg result, RegisteredStructure structure, unsigned knownLength, unsigned minCapacity)
     {
         m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure), TrustedImm32(knownLength), TrustedImm32(minCapacity));
@@ -2978,6 +2983,7 @@
     void compileCompareEqPtr(Node*);
     void compileDefineDataProperty(Node*);
     void compileDefineAccessorProperty(Node*);
+    void compileStringSlice(Node*);
     void compileToLowerCase(Node*);
     void compileThrow(Node*);
     void compileThrowStaticError(Node*);
@@ -3040,6 +3046,7 @@
     void emitGetLength(CodeOrigin, GPRReg lengthGPR, bool includeThis = false);
     void emitGetCallee(CodeOrigin, GPRReg calleeGPR);
     void emitGetArgumentStart(CodeOrigin, GPRReg startGPR);
+    void emitPopulateSliceIndex(Edge&, GPRReg length, GPRReg result);
     
     // Generate an OSR exit fuzz check. Returns Jump() if OSR exit fuzz is not enabled, or if
     // it's in training mode.

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -2853,6 +2853,11 @@
         break;
     }
     
+    case StringSlice: {
+        compileStringSlice(node);
+        break;
+    }
+
     case ToLowerCase: {
         compileToLowerCase(node);
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -5264,6 +5264,11 @@
         compileWeakMapGet(node);
         break;
 
+    case StringSlice: {
+        compileStringSlice(node);
+        break;
+    }
+
     case ToLowerCase: {
         compileToLowerCase(node);
         break;

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -291,6 +291,7 @@
     case CompareStrictEq:
     case DefineDataProperty:
     case DefineAccessorProperty:
+    case StringSlice:
     case ToLowerCase:
     case NumberToStringWithRadix:
     case NumberToStringWithValidRadixConstant:

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -1175,6 +1175,9 @@
         case Unreachable:
             compileUnreachable();
             break;
+        case StringSlice:
+            compileStringSlice();
+            break;
         case ToLowerCase:
             compileToLowerCase();
             break;
@@ -4301,6 +4304,25 @@
         }
     }
 
+    std::pair<LValue, LValue> populateSliceRange(LValue start, LValue end, LValue length)
+    {
+        // end can be nullptr.
+        ASSERT(start);
+        ASSERT(length);
+
+        auto pickIndex = [&] (LValue index) {
+            return m_out.select(m_out.greaterThanOrEqual(index, m_out.int32Zero),
+                m_out.select(m_out.above(index, length), length, index),
+                m_out.select(m_out.lessThan(m_out.add(length, index), m_out.int32Zero), m_out.int32Zero, m_out.add(length, index)));
+        };
+
+        LValue endBoundary = length;
+        if (end)
+            endBoundary = pickIndex(end);
+        LValue startIndex = pickIndex(start);
+        return std::make_pair(startIndex, endBoundary);
+    }
+
     void compileArraySlice()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
@@ -4307,22 +4329,15 @@
 
         LValue sourceStorage = lowStorage(m_node->numChildren() == 3 ? m_graph.varArgChild(m_node, 2) : m_graph.varArgChild(m_node, 3));
         LValue inputLength = m_out.load32(sourceStorage, m_heaps.Butterfly_publicLength);
+        LValue start = lowInt32(m_graph.varArgChild(m_node, 1));
+        LValue end = nullptr;
+        if (m_node->numChildren() != 3)
+            end = lowInt32(m_graph.varArgChild(m_node, 2));
 
-        LValue endBoundary;
-        if (m_node->numChildren() == 3)
-            endBoundary = m_out.load32(sourceStorage, m_heaps.Butterfly_publicLength);
-        else {
-            endBoundary = lowInt32(m_graph.varArgChild(m_node, 2));
-            endBoundary = m_out.select(m_out.greaterThanOrEqual(endBoundary, m_out.constInt32(0)),
-                m_out.select(m_out.above(endBoundary, inputLength), inputLength, endBoundary),
-                m_out.select(m_out.lessThan(m_out.add(inputLength, endBoundary), m_out.constInt32(0)), m_out.constInt32(0), m_out.add(inputLength, endBoundary)));
-        }
+        auto range = populateSliceRange(start, end, inputLength);
+        LValue startIndex = range.first;
+        LValue endBoundary = range.second;
 
-        LValue startIndex = lowInt32(m_graph.varArgChild(m_node, 1));
-        startIndex = m_out.select(m_out.greaterThanOrEqual(startIndex, m_out.constInt32(0)),
-            m_out.select(m_out.above(startIndex, inputLength), inputLength, startIndex),
-            m_out.select(m_out.lessThan(m_out.add(inputLength, startIndex), m_out.constInt32(0)), m_out.constInt32(0), m_out.add(inputLength, startIndex)));
-
         LValue resultLength = m_out.select(m_out.below(startIndex, endBoundary),
             m_out.sub(endBoundary, startIndex),
             m_out.constInt32(0));
@@ -10703,6 +10718,88 @@
         nonSpeculativeCompare(intFunctor, fallbackFunction);
     }
 
+    void compileStringSlice()
+    {
+        LBasicBlock emptyCase = m_out.newBlock();
+        LBasicBlock notEmptyCase = m_out.newBlock();
+        LBasicBlock _oneCharCase_ = m_out.newBlock();
+        LBasicBlock bitCheckCase = m_out.newBlock();
+        LBasicBlock is8Bit = m_out.newBlock();
+        LBasicBlock is16Bit = m_out.newBlock();
+        LBasicBlock bitsContinuation = m_out.newBlock();
+        LBasicBlock bigCharacter = m_out.newBlock();
+        LBasicBlock slowCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        LValue string = lowString(m_node->child1());
+        LValue length = m_out.load32NonNegative(string, m_heaps.JSString_length);
+        LValue start = lowInt32(m_node->child2());
+        LValue end = nullptr;
+        if (m_node->child3())
+            end = lowInt32(m_node->child3());
+
+        auto range = populateSliceRange(start, end, length);
+        LValue from = range.first;
+        LValue to = range.second;
+
+        LValue span = m_out.sub(to, from);
+        m_out.branch(m_out.lessThanOrEqual(span, m_out.int32Zero), unsure(emptyCase), unsure(notEmptyCase));
+
+        Vector<ValueFromBlock, 4> results;
+
+        LBasicBlock lastNext = m_out.appendTo(emptyCase, notEmptyCase);
+        results.append(m_out.anchor(m_out.weakPointer(m_graph, jsEmptyString(&vm()))));
+        m_out.jump(continuation);
+
+        m_out.appendTo(notEmptyCase, oneCharCase);
+        m_out.branch(m_out.equal(span, m_out.int32One), unsure(oneCharCase), unsure(slowCase));
+
+        m_out.appendTo(oneCharCase, bitCheckCase);
+        LValue stringImpl = m_out.loadPtr(string, m_heaps.JSString_value);
+        m_out.branch(m_out.isNull(stringImpl), unsure(slowCase), unsure(bitCheckCase));
+
+        m_out.appendTo(bitCheckCase, is8Bit);
+        LValue storage = m_out.loadPtr(stringImpl, m_heaps.StringImpl_data);
+        m_out.branch(
+            m_out.testIsZero32(
+                m_out.load32(stringImpl, m_heaps.StringImpl_hashAndFlags),
+                m_out.constInt32(StringImpl::flagIs8Bit())),
+            unsure(is16Bit), unsure(is8Bit));
+
+        m_out.appendTo(is8Bit, is16Bit);
+        // FIXME: Need to cage strings!
+        // https://bugs.webkit.org/show_bug.cgi?id=174924
+        ValueFromBlock char8Bit = m_out.anchor(m_out.load8ZeroExt32(m_out.baseIndex(m_heaps.characters8, storage, m_out.zeroExtPtr(from))));
+        m_out.jump(bitsContinuation);
+
+        m_out.appendTo(is16Bit, bigCharacter);
+        LValue char16BitValue = m_out.load16ZeroExt32(m_out.baseIndex(m_heaps.characters16, storage, m_out.zeroExtPtr(from)));
+        ValueFromBlock char16Bit = m_out.anchor(char16BitValue);
+        m_out.branch(
+            m_out.aboveOrEqual(char16BitValue, m_out.constInt32(0x100)),
+            rarely(bigCharacter), usually(bitsContinuation));
+
+        m_out.appendTo(bigCharacter, bitsContinuation);
+        results.append(m_out.anchor(vmCall(
+            Int64, m_out.operation(operationSingleCharacterString),
+            m_callFrame, char16BitValue)));
+        m_out.jump(continuation);
+
+        m_out.appendTo(bitsContinuation, slowCase);
+        LValue character = m_out.phi(Int32, char8Bit, char16Bit);
+        LValue smallStrings = m_out.constIntPtr(vm().smallStrings.singleCharacterStrings());
+        results.append(m_out.anchor(m_out.loadPtr(m_out.baseIndex(
+            m_heaps.singleCharacterStrings, smallStrings, m_out.zeroExtPtr(character)))));
+        m_out.jump(continuation);
+
+        m_out.appendTo(slowCase, continuation);
+        results.append(m_out.anchor(vmCall(pointerType(), m_out.operation(operationStringSubstr), m_callFrame, string, from, span)));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setJSValue(m_out.phi(pointerType(), results));
+    }
+
     void compileToLowerCase()
     {
         LBasicBlock notRope = m_out.newBlock();

Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (224275 => 224276)


--- trunk/Source/_javascript_Core/jit/JITOperations.h	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h	2017-11-01 13:25:21 UTC (rev 224276)
@@ -212,6 +212,7 @@
 typedef JSCell* (JIT_OPERATION *C_JITOperation_EStRZJsf)(ExecState*, Structure*, Register*, int32_t, JSFunction*);
 typedef JSCell* (JIT_OPERATION *C_JITOperation_EStZ)(ExecState*, Structure*, int32_t);
 typedef JSCell* (JIT_OPERATION *C_JITOperation_EStZZ)(ExecState*, Structure*, int32_t, int32_t);
+typedef JSCell* (JIT_OPERATION *C_JITOperation_ECZZ)(ExecState*, JSCell*, int32_t, int32_t);
 typedef JSCell* (JIT_OPERATION *C_JITOperation_EZ)(ExecState*, int32_t);
 typedef JSCell* (JIT_OPERATION *C_JITOperation_EJscI)(ExecState*, JSScope*, UniquedStringImpl*);
 typedef JSCell* (JIT_OPERATION *C_JITOperation_ECJZ)(ExecState*, JSCell*, EncodedJSValue, int32_t);

Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/runtime/Intrinsic.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -121,6 +121,8 @@
         return "StringPrototypeReplaceIntrinsic";
     case StringPrototypeReplaceRegExpIntrinsic:
         return "StringPrototypeReplaceRegExpIntrinsic";
+    case StringPrototypeSliceIntrinsic:
+        return "StringPrototypeSliceIntrinsic";
     case StringPrototypeToLowerCaseIntrinsic:
         return "StringPrototypeToLowerCaseIntrinsic";
     case NumberPrototypeToStringIntrinsic:

Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.h (224275 => 224276)


--- trunk/Source/_javascript_Core/runtime/Intrinsic.h	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.h	2017-11-01 13:25:21 UTC (rev 224276)
@@ -73,6 +73,7 @@
     StringPrototypeValueOfIntrinsic,
     StringPrototypeReplaceIntrinsic,
     StringPrototypeReplaceRegExpIntrinsic,
+    StringPrototypeSliceIntrinsic,
     StringPrototypeToLowerCaseIntrinsic,
     NumberPrototypeToStringIntrinsic,
     IMulIntrinsic,

Modified: trunk/Source/_javascript_Core/runtime/StringPrototype.cpp (224275 => 224276)


--- trunk/Source/_javascript_Core/runtime/StringPrototype.cpp	2017-11-01 12:22:53 UTC (rev 224275)
+++ trunk/Source/_javascript_Core/runtime/StringPrototype.cpp	2017-11-01 13:25:21 UTC (rev 224276)
@@ -139,7 +139,7 @@
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", stringProtoFuncLastIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingRegExpPrivateName(), stringProtoFuncReplaceUsingRegExp, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, StringPrototypeReplaceRegExpIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingStringSearchPrivateName(), stringProtoFuncReplaceUsingStringSearch, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, StringPrototypeSliceIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substr", stringProtoFuncSubstr, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substring", stringProtoFuncSubstring, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, StringPrototypeToLowerCaseIntrinsic);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to