https://bugs.webkit.org/show_bug.cgi?id=180988
Reviewed by Mark Lam.
RegExp.prototype.@@match has a fast path, @regExpMatchFast. This patch annotates this function
with RegExpMatchFastIntrinsic, and introduces RegExpMatch DFG node. This paves the way to
make NewRegexp PhantomNewRegexp if it is not used except for setting/getting its lastIndex property.
To improve RegExp.prototype.@@match's performance more, we make this builtin function small by moving
slow path part to `@matchSlow()` private function.
It improves SixSpeed regex-u.{es5,es6} largely since they stress String.prototype.match, which calls
this regExpMatchFast function.
baseline patched
regex-u.es5 55.3835+-6.3002 ^ 36.2431+-2.0797 ^ definitely 1.5281x faster
regex-u.es6 110.4624+-6.2896 ^ 94.1012+-7.2433 ^ definitely 1.1739x faster
* builtins/RegExpPrototype.js:
(globalPrivate.matchSlow):
(overriddenName.string_appeared_here.match):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* 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/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileRegExpMatch):
* dfg/DFGSpeculativeJIT.h:
* 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::compileRegExpMatch):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/RegExpPrototype.cpp:
(JSC::regExpProtoFuncMatchFast):
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@226775
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2018-01-11 Yusuke Suzuki <utatane.tea@gmail.com>
+
+ [DFG][FTL] regExpMatchFast should be handled
+ https://bugs.webkit.org/show_bug.cgi?id=180988
+
+ Reviewed by Mark Lam.
+
+ RegExp.prototype.@@match has a fast path, @regExpMatchFast. This patch annotates this function
+ with RegExpMatchFastIntrinsic, and introduces RegExpMatch DFG node. This paves the way to
+ make NewRegexp PhantomNewRegexp if it is not used except for setting/getting its lastIndex property.
+
+ To improve RegExp.prototype.@@match's performance more, we make this builtin function small by moving
+ slow path part to `@matchSlow()` private function.
+
+ It improves SixSpeed regex-u.{es5,es6} largely since they stress String.prototype.match, which calls
+ this regExpMatchFast function.
+
+ baseline patched
+
+ regex-u.es5 55.3835+-6.3002 ^ 36.2431+-2.0797 ^ definitely 1.5281x faster
+ regex-u.es6 110.4624+-6.2896 ^ 94.1012+-7.2433 ^ definitely 1.1739x faster
+
+ * builtins/RegExpPrototype.js:
+ (globalPrivate.matchSlow):
+ (overriddenName.string_appeared_here.match):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * 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/DFGNode.h:
+ (JSC::DFG::Node::hasHeapPrediction):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileRegExpMatch):
+ * dfg/DFGSpeculativeJIT.h:
+ * 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::compileRegExpMatch):
+ * runtime/Intrinsic.cpp:
+ (JSC::intrinsicName):
+ * runtime/Intrinsic.h:
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::init):
+ * runtime/RegExpPrototype.cpp:
+ (JSC::regExpProtoFuncMatchFast):
+
2018-01-11 Saam Barati <sbarati@apple.com>
Our for-in caching is wrong when we add indexed properties on things in the prototype chain
return !@isRegExpObject(regexp);
}
-@overriddenName="[Symbol.match]"
-function match(strArg)
+@globalPrivate
+function matchSlow(regexp, str)
{
"use strict";
- if (!@isObject(this))
- @throwTypeError("RegExp.prototype.@@match requires that |this| be an Object");
-
- let regexp = this;
-
- // Check for observable side effects and call the fast path if there aren't any.
- if (!@hasObservableSideEffectsForRegExpMatch(regexp))
- return @regExpMatchFast.@call(regexp, strArg);
-
- let str = @toString(strArg);
-
if (!regexp.global)
return @regExpExec(regexp, str);
}
}
+@overriddenName="[Symbol.match]"
+function match(strArg)
+{
+ "use strict";
+
+ if (!@isObject(this))
+ @throwTypeError("RegExp.prototype.@@match requires that |this| be an Object");
+
+ let str = @toString(strArg);
+
+ // Check for observable side effects and call the fast path if there aren't any.
+ if (!@hasObservableSideEffectsForRegExpMatch(this))
+ return @regExpMatchFast.@call(this, str);
+ return @matchSlow(this, str);
+}
+
@overriddenName="[Symbol.replace]"
function replace(strArg, replace)
{
clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).setType(SpecBoolean);
break;
+
+ case RegExpMatchFast:
+ ASSERT(node->child2().useKind() == RegExpObjectUse);
+ ASSERT(node->child3().useKind() == StringUse);
+ forNode(node).setType(m_graph, SpecOther | SpecArray);
+ break;
case StringReplace:
case StringReplaceRegExp:
return true;
}
+ case RegExpMatchFastIntrinsic: {
+ RELEASE_ASSERT(argumentCountIncludingThis == 2);
+
+ insertChecks();
+ Node* regExpMatch = addToGraph(RegExpMatchFast, OpInfo(0), OpInfo(prediction), addToGraph(GetGlobalObject, callee), get(virtualRegisterForArgument(0, registerOffset)), get(virtualRegisterForArgument(1, registerOffset)));
+ set(VirtualRegister(resultOperand), regExpMatch);
+ return true;
+ }
+
case ObjectGetPrototypeOfIntrinsic: {
if (argumentCountIncludingThis != 2)
return false;
case RegExpExec:
case RegExpTest:
+ case RegExpMatchFast:
if (node->child2().useKind() == RegExpObjectUse
&& node->child3().useKind() == StringUse) {
read(RegExpState);
case CheckStringIdent:
case RegExpExec:
case RegExpTest:
+ case RegExpMatchFast:
case CompareLess:
case CompareLessEq:
case CompareGreater:
break;
}
+ case RegExpMatchFast: {
+ fixEdge<KnownCellUse>(node->child1());
+ fixEdge<RegExpObjectUse>(node->child2());
+ fixEdge<StringUse>(node->child3());
+ break;
+ }
+
case StringReplace:
case StringReplaceRegExp: {
if (node->child2()->shouldSpeculateString()) {
case ArrayPush:
case RegExpExec:
case RegExpTest:
+ case RegExpMatchFast:
case GetGlobalVar:
case GetGlobalLexicalVariable:
case StringReplace:
/* Optimizations for regular expression matching. */\
macro(RegExpExec, NodeResultJS | NodeMustGenerate) \
macro(RegExpTest, NodeResultJS | NodeMustGenerate) \
+ macro(RegExpMatchFast, NodeResultJS | NodeMustGenerate) \
macro(StringReplace, NodeResultJS | NodeMustGenerate) \
macro(StringReplaceRegExp, NodeResultJS | NodeMustGenerate) \
\
return JSValue::encode(asRegExpObject(base)->exec(exec, globalObject, input));
}
+EncodedJSValue JIT_OPERATION operationRegExpMatchFastString(ExecState* exec, JSGlobalObject* globalObject, RegExpObject* regExpObject, JSString* argument)
+{
+ SuperSamplerScope superSamplerScope(false);
+
+ VM& vm = globalObject->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ if (!regExpObject->regExp()->global())
+ return JSValue::encode(regExpObject->execInline(exec, globalObject, argument));
+ return JSValue::encode(regExpObject->matchGlobal(exec, globalObject, argument));
+}
+
EncodedJSValue JIT_OPERATION operationParseIntNoRadixGeneric(ExecState* exec, EncodedJSValue value)
{
VM& vm = exec->vm();
EncodedJSValue JIT_OPERATION operationRegExpExecString(ExecState*, JSGlobalObject*, RegExpObject*, JSString*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationRegExpExec(ExecState*, JSGlobalObject*, RegExpObject*, EncodedJSValue) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationRegExpExecGeneric(ExecState*, JSGlobalObject*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationRegExpMatchFastString(ExecState*, JSGlobalObject*, RegExpObject*, JSString*) WTF_INTERNAL;
// These comparisons return a boolean within a size_t such that the value is zero extended to fill the register.
size_t JIT_OPERATION operationRegExpTestString(ExecState*, JSGlobalObject*, RegExpObject*, JSString*) WTF_INTERNAL;
size_t JIT_OPERATION operationRegExpTest(ExecState*, JSGlobalObject*, RegExpObject*, EncodedJSValue) WTF_INTERNAL;
case ArrayPush:
case RegExpExec:
case RegExpTest:
+ case RegExpMatchFast:
case StringReplace:
case StringReplaceRegExp:
case GetById:
case CheckStringIdent:
case RegExpExec:
case RegExpTest:
+ case RegExpMatchFast:
case CompareLess:
case CompareLessEq:
case CompareGreater:
m_jit.decrementSuperSamplerCount();
}
+void SpeculativeJIT::compileRegExpMatchFast(Node* node)
+{
+ SpeculateCellOperand globalObject(this, node->child1());
+ SpeculateCellOperand base(this, node->child2());
+ SpeculateCellOperand argument(this, node->child3());
+ GPRReg globalObjectGPR = globalObject.gpr();
+ GPRReg baseGPR = base.gpr();
+ GPRReg argumentGPR = argument.gpr();
+ speculateRegExpObject(node->child2(), baseGPR);
+ speculateString(node->child3(), argumentGPR);
+
+ flushRegisters();
+ JSValueRegsFlushedCallResult result(this);
+ JSValueRegs resultRegs = result.regs();
+ callOperation(
+ operationRegExpMatchFastString, resultRegs,
+ globalObjectGPR, baseGPR, argumentGPR);
+ m_jit.exceptionCheck();
+
+ jsValueResult(resultRegs, node);
+}
+
void SpeculativeJIT::compileLazyJSConstant(Node* node)
{
JSValueRegsTemporary result(this);
void compileArrayPush(Node*);
void compileNotifyWrite(Node*);
void compileRegExpExec(Node*);
+ void compileRegExpMatchFast(Node*);
void compileRegExpTest(Node*);
void compileStringReplace(Node*);
void compileIsObjectOrNull(Node*);
break;
}
+ case RegExpMatchFast: {
+ compileRegExpMatchFast(node);
+ break;
+ }
+
case StringReplace:
case StringReplaceRegExp: {
compileStringReplace(node);
break;
}
+ case RegExpMatchFast: {
+ compileRegExpMatchFast(node);
+ break;
+ }
+
case StringReplace:
case StringReplaceRegExp: {
compileStringReplace(node);
case GetRestLength:
case RegExpExec:
case RegExpTest:
+ case RegExpMatchFast:
case NewRegexp:
case StringReplace:
case StringReplaceRegExp:
case RegExpTest:
compileRegExpTest();
break;
+ case RegExpMatchFast:
+ compileRegExpMatchFast();
+ break;
case NewRegexp:
compileNewRegexp();
break;
setBoolean(result);
}
+ void compileRegExpMatchFast()
+ {
+ LValue globalObject = lowCell(m_node->child1());
+ LValue base = lowRegExpObject(m_node->child2());
+ LValue argument = lowString(m_node->child3());
+ LValue result = vmCall(
+ Int64, m_out.operation(operationRegExpMatchFastString), m_callFrame, globalObject,
+ base, argument);
+ setJSValue(result);
+ }
+
void compileNewRegexp()
{
FrozenValue* regexp = m_node->cellOperand();
return "RegExpTestIntrinsic";
case RegExpTestFastIntrinsic:
return "RegExpTestFastIntrinsic";
+ case RegExpMatchFastIntrinsic:
+ return "RegExpMatchFastIntrinsic";
case ObjectGetPrototypeOfIntrinsic:
return "ObjectGetPrototypeOfIntrinsic";
case ReflectGetPrototypeOfIntrinsic:
RegExpExecIntrinsic,
RegExpTestIntrinsic,
RegExpTestFastIntrinsic,
+ RegExpMatchFastIntrinsic,
ObjectGetPrototypeOfIntrinsic,
ReflectGetPrototypeOfIntrinsic,
StringPrototypeValueOfIntrinsic,
// RegExp.prototype helpers.
GlobalPropertyInfo(vm.propertyNames->builtinNames().regExpBuiltinExecPrivateName(), builtinRegExpExec, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
GlobalPropertyInfo(vm.propertyNames->builtinNames().regExpCreatePrivateName(), JSFunction::create(vm, this, 2, String(), esSpecRegExpCreate, NoIntrinsic), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
- GlobalPropertyInfo(vm.propertyNames->builtinNames().regExpMatchFastPrivateName(), JSFunction::create(vm, this, 1, String(), regExpProtoFuncMatchFast), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
+ GlobalPropertyInfo(vm.propertyNames->builtinNames().regExpMatchFastPrivateName(), JSFunction::create(vm, this, 1, String(), regExpProtoFuncMatchFast, RegExpMatchFastIntrinsic), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
GlobalPropertyInfo(vm.propertyNames->builtinNames().regExpSearchFastPrivateName(), JSFunction::create(vm, this, 1, String(), regExpProtoFuncSearchFast), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
GlobalPropertyInfo(vm.propertyNames->builtinNames().regExpSplitFastPrivateName(), JSFunction::create(vm, this, 2, String(), regExpProtoFuncSplitFast), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
GlobalPropertyInfo(vm.propertyNames->builtinNames().regExpPrototypeSymbolReplacePrivateName(), m_regExpPrototype->getDirect(vm, vm.propertyNames->replaceSymbol), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
EncodedJSValue JSC_HOST_CALL regExpProtoFuncMatchFast(ExecState* exec)
{
- VM& vm = exec->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- JSValue thisValue = exec->thisValue();
- if (!thisValue.inherits(vm, RegExpObject::info()))
- return throwVMTypeError(exec, scope);
- JSString* string = exec->argument(0).toStringOrNull(exec);
- EXCEPTION_ASSERT(!!scope.exception() == !string);
- if (!string)
- return encodedJSValue();
- if (!asRegExpObject(thisValue)->regExp()->global()) {
- scope.release();
- return JSValue::encode(asRegExpObject(thisValue)->exec(exec, exec->lexicalGlobalObject(), string));
- }
- scope.release();
- return JSValue::encode(asRegExpObject(thisValue)->matchGlobal(exec, exec->lexicalGlobalObject(), string));
+ RegExpObject* thisObject = asRegExpObject(exec->thisValue());
+ JSString* string = jsCast<JSString*>(exec->uncheckedArgument(0));
+ if (!thisObject->regExp()->global())
+ return JSValue::encode(thisObject->exec(exec, exec->lexicalGlobalObject(), string));
+ return JSValue::encode(thisObject->matchGlobal(exec, exec->lexicalGlobalObject(), string));
}
EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec)