Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (226133 => 226134)
--- trunk/Source/_javascript_Core/ChangeLog 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-12-19 19:38:31 UTC (rev 226134)
@@ -1,5 +1,50 @@
2017-12-19 Yusuke Suzuki <[email protected]>
+ [DFG][FTL] NewRegexp shoud be fast
+ https://bugs.webkit.org/show_bug.cgi?id=180960
+
+ Reviewed by Michael Saboff.
+
+ When we encounter RegExp literal like /AAA/g, we need to create a RegExp object.
+ Typical idiom like `string.match(/regexp/)` requires RegExp object creation
+ every time.
+
+ As a first step, this patch accelerates RegExp object creation by handling it
+ in DFG and FTL. In a subsequent patch, we would like to introduce PhantomNewRegexp
+ to remove unnecessary RegExp object creations.
+
+ This patch improves SixSpeed/regex-u.{es5,es6}.
+
+ baseline patched
+
+ regex-u.es5 69.6759+-3.1951 ^ 53.1425+-2.0292 ^ definitely 1.3111x faster
+ regex-u.es6 129.5413+-5.4437 ^ 107.2105+-7.7775 ^ definitely 1.2083x faster
+
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileNewRegexp):
+ * dfg/DFGSpeculativeJIT.h:
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLAbstractHeapRepository.h:
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNewRegexp):
+ * jit/JIT.h:
+ * jit/JITInlines.h:
+ (JSC::JIT::callOperation):
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_new_regexp):
+ * jit/JITOperations.cpp:
+ * jit/JITOperations.h:
+ * llint/LLIntSlowPaths.cpp:
+ (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+ * runtime/RegExpObject.h:
+ (JSC::RegExpObject::offsetOfRegExp):
+ (JSC::RegExpObject::allocationSize):
+
+2017-12-19 Yusuke Suzuki <[email protected]>
+
Unreviewed, include YarrErrorCode.h in Yarr.h
https://bugs.webkit.org/show_bug.cgi?id=180966
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (226133 => 226134)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-12-19 19:38:31 UTC (rev 226134)
@@ -9124,6 +9124,50 @@
cellResult(resultGPR, node);
}
+void SpeculativeJIT::compileNewRegexp(Node* node)
+{
+ RegExp* regexp = node->castOperand<RegExp*>();
+
+ // FIXME: If the RegExp is invalid, we should emit a different bytecode.
+ // https://bugs.webkit.org/show_bug.cgi?id=180970
+ if (!regexp->isValid()) {
+ flushRegisters();
+ GPRFlushedCallResult result(this);
+
+ callOperation(operationNewRegexp, result.gpr(), regexp);
+ m_jit.exceptionCheck();
+
+ cellResult(result.gpr(), node);
+ return;
+ }
+
+ GPRTemporary result(this);
+ GPRTemporary scratch1(this);
+ GPRTemporary scratch2(this);
+
+ GPRReg resultGPR = result.gpr();
+ GPRReg scratch1GPR = scratch1.gpr();
+ GPRReg scratch2GPR = scratch2.gpr();
+
+ JITCompiler::JumpList slowPath;
+
+ auto structure = m_jit.graph().registerStructure(m_jit.graph().globalObjectFor(node->origin.semantic)->regExpStructure());
+ auto butterfly = TrustedImmPtr(nullptr);
+ auto mask = TrustedImm32(0);
+ emitAllocateJSObject<RegExpObject>(resultGPR, TrustedImmPtr(structure), butterfly, mask, scratch1GPR, scratch2GPR, slowPath);
+
+ m_jit.storePtr(
+ TrustedImmPtr(node->cellOperand()),
+ CCallHelpers::Address(resultGPR, RegExpObject::offsetOfRegExp()));
+ m_jit.storeTrustedValue(jsNumber(0), CCallHelpers::Address(resultGPR, RegExpObject::offsetOfLastIndex()));
+ m_jit.store8(TrustedImm32(true), CCallHelpers::Address(resultGPR, RegExpObject::offsetOfLastIndexIsWritable()));
+ m_jit.mutatorFence(*m_jit.vm());
+
+ addSlowPathGenerator(slowPathCall(slowPath, this, operationNewRegexp, resultGPR, regexp));
+
+ cellResult(resultGPR, node);
+}
+
void SpeculativeJIT::speculateCellTypeWithoutTypeFiltering(
Edge edge, GPRReg cellGPR, JSType jsType)
{
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (226133 => 226134)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2017-12-19 19:38:31 UTC (rev 226134)
@@ -2999,6 +2999,7 @@
template <typename ClassType> void compileNewFunctionCommon(GPRReg, RegisteredStructure, GPRReg, GPRReg, GPRReg, MacroAssembler::JumpList&, size_t, FunctionExecutable*, ptrdiff_t, ptrdiff_t, ptrdiff_t);
void compileNewFunction(Node*);
void compileSetFunctionName(Node*);
+ void compileNewRegexp(Node*);
void compileForwardVarargs(Node*);
void compileCreateActivation(Node*);
void compileCreateDirectArguments(Node*);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (226133 => 226134)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-12-19 19:38:31 UTC (rev 226134)
@@ -4002,16 +4002,7 @@
}
case NewRegexp: {
- flushRegisters();
- GPRFlushedCallResult resultPayload(this);
- GPRFlushedCallResult2 resultTag(this);
-
- RegExp* regexp = node->castOperand<RegExp*>();
- callOperation(operationNewRegexp, JSValueRegs(resultTag.gpr(), resultPayload.gpr()), regexp);
- m_jit.exceptionCheck();
-
- // FIXME: make the callOperation above explicitly return a cell result, or jitAssert the tag is a cell tag.
- cellResult(resultPayload.gpr(), node);
+ compileNewRegexp(node);
break;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (226133 => 226134)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-12-19 19:38:31 UTC (rev 226134)
@@ -4220,14 +4220,7 @@
}
case NewRegexp: {
- flushRegisters();
- GPRFlushedCallResult result(this);
-
- RegExp* regexp = node->castOperand<RegExp*>();
- callOperation(operationNewRegexp, result.gpr(), regexp);
- m_jit.exceptionCheck();
-
- cellResult(result.gpr(), node);
+ compileNewRegexp(node);
break;
}
Modified: trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h (226133 => 226134)
--- trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h 2017-12-19 19:38:31 UTC (rev 226134)
@@ -90,6 +90,7 @@
macro(RegExpConstructor_cachedResult_result_start, RegExpConstructor::offsetOfCachedResult() + RegExpCachedResult::offsetOfResult() + OBJECT_OFFSETOF(MatchResult, start)) \
macro(RegExpConstructor_cachedResult_result_end, RegExpConstructor::offsetOfCachedResult() + RegExpCachedResult::offsetOfResult() + OBJECT_OFFSETOF(MatchResult, end)) \
macro(RegExpConstructor_cachedResult_reified, RegExpConstructor::offsetOfCachedResult() + RegExpCachedResult::offsetOfReified()) \
+ macro(RegExpObject_regExp, RegExpObject::offsetOfRegExp()) \
macro(RegExpObject_lastIndex, RegExpObject::offsetOfLastIndex()) \
macro(RegExpObject_lastIndexIsWritable, RegExpObject::offsetOfLastIndexIsWritable()) \
macro(ShadowChicken_Packet_callee, OBJECT_OFFSETOF(ShadowChicken::Packet, callee)) \
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (226133 => 226134)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-12-19 19:38:31 UTC (rev 226134)
@@ -78,6 +78,7 @@
#include "JSLexicalEnvironment.h"
#include "JSMap.h"
#include "OperandsInlines.h"
+#include "RegExpObject.h"
#include "ScopedArguments.h"
#include "ScopedArgumentsTable.h"
#include "ScratchRegisterAllocator.h"
@@ -10267,12 +10268,47 @@
{
FrozenValue* regexp = m_node->cellOperand();
ASSERT(regexp->cell()->inherits(vm(), RegExp::info()));
- LValue result = vmCall(
- pointerType(),
- m_out.operation(operationNewRegexp), m_callFrame,
- frozenPointer(regexp));
-
- setJSValue(result);
+
+ // FIXME: If the RegExp is invalid, we should emit a different bytecode.
+ // https://bugs.webkit.org/show_bug.cgi?id=180970
+ if (!m_node->castOperand<RegExp*>()->isValid()) {
+ LValue result = vmCall(
+ pointerType(),
+ m_out.operation(operationNewRegexp), m_callFrame,
+ frozenPointer(regexp));
+
+ setJSValue(result);
+ return;
+ }
+
+ LBasicBlock slowCase = m_out.newBlock();
+ LBasicBlock continuation = m_out.newBlock();
+
+ LBasicBlock lastNext = m_out.insertNewBlocksBefore(slowCase);
+
+ auto structure = m_graph.registerStructure(m_graph.globalObjectFor(m_node->origin.semantic)->regExpStructure());
+ LValue fastResultValue = allocateObject<RegExpObject>(structure, m_out.intPtrZero, m_out.int32Zero, slowCase);
+ m_out.storePtr(frozenPointer(regexp), fastResultValue, m_heaps.RegExpObject_regExp);
+ m_out.store64(m_out.constInt64(JSValue::encode(jsNumber(0))), fastResultValue, m_heaps.RegExpObject_lastIndex);
+ m_out.store32As8(m_out.constInt32(true), m_out.address(fastResultValue, m_heaps.RegExpObject_lastIndexIsWritable));
+ mutatorFence();
+ ValueFromBlock fastResult = m_out.anchor(fastResultValue);
+ m_out.jump(continuation);
+
+ m_out.appendTo(slowCase, continuation);
+ VM& vm = this->vm();
+ RegExp* regexpCell = regexp->cast<RegExp*>();
+ LValue slowResultValue = lazySlowPath(
+ [=, &vm] (const Vector<Location>& locations) -> RefPtr<LazySlowPath::Generator> {
+ return createLazyCallGenerator(vm,
+ operationNewRegexp, locations[0].directGPR(),
+ CCallHelpers::TrustedImmPtr(regexpCell));
+ });
+ ValueFromBlock slowResult = m_out.anchor(slowResultValue);
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ setJSValue(m_out.phi(pointerType(), fastResult, slowResult));
}
void compileSetFunctionName()
Modified: trunk/Source/_javascript_Core/jit/JIT.h (226133 => 226134)
--- trunk/Source/_javascript_Core/jit/JIT.h 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/jit/JIT.h 2017-12-19 19:38:31 UTC (rev 226134)
@@ -719,6 +719,7 @@
MacroAssembler::Call callOperation(C_JITOperation_EL, GPRReg);
MacroAssembler::Call callOperation(C_JITOperation_EL, TrustedImmPtr);
MacroAssembler::Call callOperation(C_JITOperation_ESt, Structure*);
+ MacroAssembler::Call callOperation(C_JITOperation_EC, JSCell*);
MacroAssembler::Call callOperation(C_JITOperation_EZ, int32_t);
MacroAssembler::Call callOperation(Z_JITOperation_EJZZ, GPRReg, int32_t, int32_t);
MacroAssembler::Call callOperation(J_JITOperation_E, int);
Modified: trunk/Source/_javascript_Core/jit/JITInlines.h (226133 => 226134)
--- trunk/Source/_javascript_Core/jit/JITInlines.h 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/jit/JITInlines.h 2017-12-19 19:38:31 UTC (rev 226134)
@@ -253,6 +253,12 @@
return appendCallWithExceptionCheck(operation);
}
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(C_JITOperation_EC operation, JSCell* cell)
+{
+ setupArgumentsWithExecState(TrustedImmPtr(cell));
+ return appendCallWithExceptionCheck(operation);
+}
+
ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(C_JITOperation_EZ operation, int32_t arg)
{
setupArgumentsWithExecState(TrustedImm32(arg));
Modified: trunk/Source/_javascript_Core/jit/JITOpcodes.cpp (226133 => 226134)
--- trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2017-12-19 19:38:31 UTC (rev 226134)
@@ -912,7 +912,9 @@
void JIT::emit_op_new_regexp(Instruction* currentInstruction)
{
- callOperation(operationNewRegexp, currentInstruction[1].u.operand, m_codeBlock->regexp(currentInstruction[2].u.operand));
+ int dst = currentInstruction[1].u.operand;
+ callOperation(operationNewRegexp, m_codeBlock->regexp(currentInstruction[2].u.operand));
+ emitStoreCell(dst, returnValueGPR);
}
void JIT::emitNewFuncCommon(Instruction* currentInstruction)
Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (226133 => 226134)
--- trunk/Source/_javascript_Core/jit/JITOperations.cpp 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp 2017-12-19 19:38:31 UTC (rev 226134)
@@ -1259,7 +1259,7 @@
return constructEmptyObject(exec, structure);
}
-EncodedJSValue JIT_OPERATION operationNewRegexp(ExecState* exec, void* regexpPtr)
+JSCell* JIT_OPERATION operationNewRegexp(ExecState* exec, JSCell* regexpPtr)
{
SuperSamplerScope superSamplerScope(false);
VM& vm = exec->vm();
@@ -1267,12 +1267,15 @@
auto scope = DECLARE_THROW_SCOPE(vm);
RegExp* regexp = static_cast<RegExp*>(regexpPtr);
+
+ // FIXME: If the RegExp is invalid, we should emit a different bytecode.
+ // https://bugs.webkit.org/show_bug.cgi?id=180970
if (!regexp->isValid()) {
throwException(exec, scope, createSyntaxError(exec, regexp->errorMessage()));
- return JSValue::encode(jsUndefined());
+ return nullptr;
}
- return JSValue::encode(RegExpObject::create(vm, exec->lexicalGlobalObject()->regExpStructure(), regexp));
+ return RegExpObject::create(vm, exec->lexicalGlobalObject()->regExpStructure(), regexp);
}
// The only reason for returning an UnusedPtr (instead of void) is so that we can reuse the
Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (226133 => 226134)
--- trunk/Source/_javascript_Core/jit/JITOperations.h 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h 2017-12-19 19:38:31 UTC (rev 226134)
@@ -404,7 +404,7 @@
EncodedJSValue JIT_OPERATION operationNewAsyncGeneratorFunctionWithInvalidatedReallocationWatchpoint(ExecState*, JSScope*, JSCell*) WTF_INTERNAL;
void JIT_OPERATION operationSetFunctionName(ExecState*, JSCell*, EncodedJSValue) WTF_INTERNAL;
JSCell* JIT_OPERATION operationNewObject(ExecState*, Structure*) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationNewRegexp(ExecState*, void*) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationNewRegexp(ExecState*, JSCell*) WTF_INTERNAL;
UnusedPtr JIT_OPERATION operationHandleTraps(ExecState*) WTF_INTERNAL;
void JIT_OPERATION operationThrow(ExecState*, EncodedJSValue) WTF_INTERNAL;
void JIT_OPERATION operationDebug(ExecState*, int32_t) WTF_INTERNAL;
Modified: trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp (226133 => 226134)
--- trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp 2017-12-19 19:38:31 UTC (rev 226134)
@@ -552,6 +552,9 @@
{
LLINT_BEGIN();
RegExp* regExp = exec->codeBlock()->regexp(pc[2].u.operand);
+
+ // FIXME: If the RegExp is invalid, we should emit a different bytecode.
+ // https://bugs.webkit.org/show_bug.cgi?id=180970
if (!regExp->isValid())
LLINT_THROW(createSyntaxError(exec, regExp->errorMessage()));
LLINT_RETURN(RegExpObject::create(vm, exec->lexicalGlobalObject()->regExpStructure(), regExp));
Modified: trunk/Source/_javascript_Core/runtime/RegExpObject.h (226133 => 226134)
--- trunk/Source/_javascript_Core/runtime/RegExpObject.h 2017-12-19 19:34:17 UTC (rev 226133)
+++ trunk/Source/_javascript_Core/runtime/RegExpObject.h 2017-12-19 19:38:31 UTC (rev 226134)
@@ -88,6 +88,11 @@
return Structure::create(vm, globalObject, prototype, TypeInfo(RegExpObjectType, StructureFlags), info());
}
+ static ptrdiff_t offsetOfRegExp()
+ {
+ return OBJECT_OFFSETOF(RegExpObject, m_regExp);
+ }
+
static ptrdiff_t offsetOfLastIndex()
{
return OBJECT_OFFSETOF(RegExpObject, m_lastIndex);
@@ -98,6 +103,12 @@
return OBJECT_OFFSETOF(RegExpObject, m_lastIndexIsWritable);
}
+ static size_t allocationSize(Checked<size_t> inlineCapacity)
+ {
+ ASSERT_UNUSED(inlineCapacity, !inlineCapacity);
+ return sizeof(RegExpObject);
+ }
+
static unsigned advanceStringUnicode(String, unsigned length, unsigned currentIndex);
protected:
@@ -117,7 +128,7 @@
WriteBarrier<RegExp> m_regExp;
WriteBarrier<Unknown> m_lastIndex;
- bool m_lastIndexIsWritable;
+ uint8_t m_lastIndexIsWritable;
};
RegExpObject* asRegExpObject(JSValue);