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);