[DFG][FTL] NewRegexp shoud be fast
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 Dec 2017 19:38:31 +0000 (19:38 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 Dec 2017 19:38:31 +0000 (19:38 +0000)
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):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@226134 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITInlines.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/runtime/RegExpObject.h

index 9776bed..f35d9b2 100644 (file)
@@ -1,5 +1,50 @@
 2017-12-19  Yusuke Suzuki  <utatane.tea@gmail.com>
 
+        [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  <utatane.tea@gmail.com>
+
         Unreviewed, include YarrErrorCode.h in Yarr.h
         https://bugs.webkit.org/show_bug.cgi?id=180966
 
index f87685e..f378ac3 100644 (file)
@@ -9124,6 +9124,50 @@ void SpeculativeJIT::compileNewTypedArray(Node* node)
     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)
 {
index 974fc41..497c289 100644 (file)
@@ -2999,6 +2999,7 @@ public:
     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*);
index 913717b..007ed50 100644 (file)
@@ -4002,16 +4002,7 @@ void SpeculativeJIT::compile(Node* node)
     }
         
     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;
     }
 
index 37f2aaf..1221583 100644 (file)
@@ -4220,14 +4220,7 @@ void SpeculativeJIT::compile(Node* node)
     }
         
     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;
     }
 
index 3722e82..1392197 100644 (file)
@@ -90,6 +90,7 @@ namespace JSC { namespace FTL {
     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)) \
index 5a20c0c..ef9607b 100644 (file)
@@ -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 @@ private:
     {
         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()
index c88cb82..7b284bf 100644 (file)
@@ -719,6 +719,7 @@ namespace JSC {
         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);
index 4c3111b..e35fece 100644 (file)
@@ -253,6 +253,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(C_JITOperation_ESt operati
     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));
index 84e54c1..43e3b1e 100644 (file)
@@ -912,7 +912,9 @@ void JIT::emitSlow_op_check_traps(Instruction*, Vector<SlowCaseEntry>::iterator&
 
 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)
index fa544d2..79d3c9e 100644 (file)
@@ -1259,7 +1259,7 @@ JSCell* JIT_OPERATION operationNewObject(ExecState* exec, Structure* structure)
     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 @@ EncodedJSValue JIT_OPERATION operationNewRegexp(ExecState* exec, void* regexpPtr
     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
index 7479146..fc0a15d 100644 (file)
@@ -404,7 +404,7 @@ EncodedJSValue JIT_OPERATION operationNewAsyncGeneratorFunction(ExecState*, JSSc
 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;
index d7f3b87..8264b3b 100644 (file)
@@ -552,6 +552,9 @@ LLINT_SLOW_PATH_DECL(slow_path_new_regexp)
 {
     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));
index 8a8e9bd..1d54397 100644 (file)
@@ -88,6 +88,11 @@ public:
         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 @@ public:
         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 @@ private:
 
     WriteBarrier<RegExp> m_regExp;
     WriteBarrier<Unknown> m_lastIndex;
-    bool m_lastIndexIsWritable;
+    uint8_t m_lastIndexIsWritable;
 };
 
 RegExpObject* asRegExpObject(JSValue);