Strength reduction, RegExp.exec -> RegExp.test
authorbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 18 Mar 2012 01:08:16 +0000 (01:08 +0000)
committerbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 18 Mar 2012 01:08:16 +0000 (01:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=81459

Reviewed by Sam Weinig.

RegExp.prototype.exec & RegExp.prototype.test can both be used to test a regular
expression for a match against a string - however exec is more expensive, since
it allocates a matches array object. In cases where the result is consumed in a
boolean context the allocation of the matches array can be trivially elided.

For example:
    function f()
    {
        for (i =0; i < 10000000; ++i)
            if(!/a/.exec("a"))
                err = true;
    }

This is a 2.5x speedup on this example microbenchmark loop.

In a more advanced form of this optimization, we may be able to avoid allocating
the array where access to the array can be observed.

* create_hash_table:
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsic):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileRegExpExec):
(DFG):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* jsc.cpp:
(GlobalObject::addConstructableFunction):
* runtime/Intrinsic.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::create):
(JSC):
* runtime/JSFunction.h:
(JSFunction):
* runtime/Lookup.cpp:
(JSC::setUpStaticFunctionSlot):
* runtime/RegExpObject.cpp:
(JSC::RegExpObject::exec):
(JSC::RegExpObject::match):
* runtime/RegExpObject.h:
(RegExpObject):
* runtime/RegExpPrototype.cpp:
(JSC::regExpProtoFuncTest):
(JSC::regExpProtoFuncExec):

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

21 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/create_hash_table
Source/JavaScriptCore/dfg/DFGAbstractState.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/runtime/JSFunction.h
Source/JavaScriptCore/runtime/Lookup.cpp
Source/JavaScriptCore/runtime/RegExpObject.cpp
Source/JavaScriptCore/runtime/RegExpObject.h
Source/JavaScriptCore/runtime/RegExpPrototype.cpp

index e8a9a3a..f8bea37 100644 (file)
@@ -1,3 +1,69 @@
+2012-03-17  Gavin Barraclough  <barraclough@apple.com>
+
+        Strength reduction, RegExp.exec -> RegExp.test
+        https://bugs.webkit.org/show_bug.cgi?id=81459
+
+        Reviewed by Sam Weinig.
+
+        RegExp.prototype.exec & RegExp.prototype.test can both be used to test a regular
+        expression for a match against a string - however exec is more expensive, since
+        it allocates a matches array object. In cases where the result is consumed in a
+        boolean context the allocation of the matches array can be trivially elided.
+
+        For example:
+            function f()
+            {
+                for (i =0; i < 10000000; ++i)
+                    if(!/a/.exec("a"))
+                        err = true;
+            }
+
+        This is a 2.5x speedup on this example microbenchmark loop.
+
+        In a more advanced form of this optimization, we may be able to avoid allocating
+        the array where access to the array can be observed.
+
+        * create_hash_table:
+        * dfg/DFGAbstractState.cpp:
+        (JSC::DFG::AbstractState::execute):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsic):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        * dfg/DFGNodeType.h:
+        (DFG):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileRegExpExec):
+        (DFG):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * jsc.cpp:
+        (GlobalObject::addConstructableFunction):
+        * runtime/Intrinsic.h:
+        * runtime/JSFunction.cpp:
+        (JSC::JSFunction::create):
+        (JSC):
+        * runtime/JSFunction.h:
+        (JSFunction):
+        * runtime/Lookup.cpp:
+        (JSC::setUpStaticFunctionSlot):
+        * runtime/RegExpObject.cpp:
+        (JSC::RegExpObject::exec):
+        (JSC::RegExpObject::match):
+        * runtime/RegExpObject.h:
+        (RegExpObject):
+        * runtime/RegExpPrototype.cpp:
+        (JSC::regExpProtoFuncTest):
+        (JSC::regExpProtoFuncExec):
+
 2012-03-16  Michael Saboff  <msaboff@apple.com>
 
         Improve diagnostic benefit of JSGlobalData::m_isInitializingObject
index ae49ce4..cb2809d 100755 (executable)
@@ -290,6 +290,10 @@ sub output() {
             $intrinsic = "ArrayPushIntrinsic" if ($key eq "push");
             $intrinsic = "ArrayPopIntrinsic" if ($key eq "pop");
         }
+        if ($name eq "regExpPrototypeTable") {
+            $intrinsic = "RegExpExecIntrinsic" if ($key eq "exec");
+            $intrinsic = "RegExpTestIntrinsic" if ($key eq "test");
+        }
 
         print "   { \"$key\", $attrs[$i], (intptr_t)" . $castStr . "($firstValue), (intptr_t)$secondValue, $intrinsic },\n";
         $i++;
index c93fc15..70f8a4e 100644 (file)
@@ -629,6 +629,13 @@ bool AbstractState::execute(unsigned indexInBlock)
         forNode(nodeIndex).makeTop();
         break;
             
+    case RegExpExec:
+    case RegExpTest:
+        forNode(node.child1()).filter(PredictCell);
+        forNode(node.child2()).filter(PredictCell);
+        forNode(nodeIndex).makeTop();
+        break;
+            
     case Jump:
         break;
             
index 0aac52f..2f7ebdb 100644 (file)
@@ -1379,6 +1379,28 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins
         return true;
     }
 
+    case RegExpExecIntrinsic: {
+        if (argumentCountIncludingThis != 2)
+            return false;
+        
+        NodeIndex regExpExec = addToGraph(RegExpExec, OpInfo(0), OpInfo(prediction), get(registerOffset + argumentToOperand(0)), get(registerOffset + argumentToOperand(1)));
+        if (usesResult)
+            set(resultOperand, regExpExec);
+        
+        return true;
+    }
+        
+    case RegExpTestIntrinsic: {
+        if (argumentCountIncludingThis != 2)
+            return false;
+        
+        NodeIndex regExpExec = addToGraph(RegExpTest, OpInfo(0), OpInfo(prediction), get(registerOffset + argumentToOperand(0)), get(registerOffset + argumentToOperand(1)));
+        if (usesResult)
+            set(resultOperand, regExpExec);
+        
+        return true;
+    }
+        
     default:
         return false;
     }
index 11922db..1312468 100644 (file)
@@ -492,6 +492,8 @@ struct Node {
         case ResolveGlobal:
         case ArrayPop:
         case ArrayPush:
+        case RegExpExec:
+        case RegExpTest:
             return true;
         default:
             return false;
index a457a21..c028bbe 100644 (file)
@@ -131,6 +131,10 @@ namespace JSC { namespace DFG {
     macro(ArrayPush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
     macro(ArrayPop, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
     \
+    /* Optimizations for regular expression matching. */\
+    macro(RegExpExec, NodeResultJS | NodeMustGenerate) \
+    macro(RegExpTest, NodeResultJS | NodeMustGenerate) \
+    \
     /* Optimizations for string access */ \
     macro(StringCharCodeAt, NodeResultInt32) \
     macro(StringCharAt, NodeResultJS) \
index 304c54d..9fc964c 100644 (file)
@@ -502,6 +502,28 @@ EncodedJSValue DFG_OPERATION operationArrayPush(ExecState* exec, EncodedJSValue
     array->push(exec, JSValue::decode(encodedValue));
     return JSValue::encode(jsNumber(array->length()));
 }
+
+EncodedJSValue DFG_OPERATION operationRegExpExec(ExecState* exec, JSCell* base, JSCell* argument)
+{
+    if (!base->inherits(&RegExpObject::s_info))
+        return throwVMTypeError(exec);
+
+    ASSERT(argument->isString() || argument->isObject());
+    JSString* input = argument->isString() ? asString(argument) : asObject(argument)->toString(exec);
+    return JSValue::encode(asRegExpObject(base)->exec(exec, input));
+}
+        
+size_t DFG_OPERATION operationRegExpTest(ExecState* exec, JSCell* base, JSCell* argument)
+{
+    if (!base->inherits(&RegExpObject::s_info)) {
+        throwTypeError(exec);
+        return false;
+    }
+
+    ASSERT(argument->isString() || argument->isObject());
+    JSString* input = argument->isString() ? asString(argument) : asObject(argument)->toString(exec);
+    return asRegExpObject(base)->match(exec, input);
+}
         
 EncodedJSValue DFG_OPERATION operationArrayPop(ExecState* exec, JSArray* array)
 {
index 6455bd6..0544e21 100644 (file)
@@ -60,36 +60,38 @@ extern "C" {
     I: Identifier*
     G: GlobalResolveInfo*
 */
-typedef int32_t DFG_OPERATION (*Z_DFGOperation_D)(double);
-typedef JSCell* DFG_OPERATION (*C_DFGOperation_E)(ExecState*);
-typedef JSCell* DFG_OPERATION (*C_DFGOperation_EC)(ExecState*, JSCell*);
-typedef JSCell* DFG_OPERATION (*C_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*);
 typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EA)(ExecState*, JSArray*);
-typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EJA)(ExecState*, EncodedJSValue, JSArray*);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECI)(ExecState*, JSCell*, Identifier*);
 typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECJ)(ExecState*, JSCell*, EncodedJSValue);
-typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EJJ)(ExecState*, EncodedJSValue, EncodedJSValue);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EGI)(ExecState*, GlobalResolveInfo*, Identifier*);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EI)(ExecState*, Identifier*);
 typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EJ)(ExecState*, EncodedJSValue);
-typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EJP)(ExecState*, EncodedJSValue, void*);
-typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECI)(ExecState*, JSCell*, Identifier*);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EJA)(ExecState*, EncodedJSValue, JSArray*);
 typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EJI)(ExecState*, EncodedJSValue, Identifier*);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EJJ)(ExecState*, EncodedJSValue, EncodedJSValue);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EJP)(ExecState*, EncodedJSValue, void*);
 typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EP)(ExecState*, void*);
 typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EPP)(ExecState*, void*, void*);
-typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EGI)(ExecState*, GlobalResolveInfo*, Identifier*);
 typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EPS)(ExecState*, void*, size_t);
 typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ESS)(ExecState*, size_t, size_t);
-typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EI)(ExecState*, Identifier*);
+typedef JSCell* DFG_OPERATION (*C_DFGOperation_E)(ExecState*);
+typedef JSCell* DFG_OPERATION (*C_DFGOperation_EC)(ExecState*, JSCell*);
+typedef JSCell* DFG_OPERATION (*C_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*);
+typedef double DFG_OPERATION (*D_DFGOperation_DD)(double, double);
+typedef double DFG_OPERATION (*D_DFGOperation_EJ)(ExecState*, EncodedJSValue);
+typedef int32_t DFG_OPERATION (*Z_DFGOperation_D)(double);
+typedef size_t DFG_OPERATION (*S_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*);
 typedef size_t DFG_OPERATION (*S_DFGOperation_EJ)(ExecState*, EncodedJSValue);
 typedef size_t DFG_OPERATION (*S_DFGOperation_EJJ)(ExecState*, EncodedJSValue, EncodedJSValue);
-typedef void DFG_OPERATION (*V_DFGOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
+typedef void DFG_OPERATION (*V_DFGOperation_EAZJ)(ExecState*, JSArray*, int32_t, EncodedJSValue);
 typedef void DFG_OPERATION (*V_DFGOperation_ECJJ)(ExecState*, JSCell*, EncodedJSValue, EncodedJSValue);
-typedef void DFG_OPERATION (*V_DFGOperation_EJPP)(ExecState*, EncodedJSValue, EncodedJSValue, void*);
 typedef void DFG_OPERATION (*V_DFGOperation_EJCI)(ExecState*, EncodedJSValue, JSCell*, Identifier*);
+typedef void DFG_OPERATION (*V_DFGOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
+typedef void DFG_OPERATION (*V_DFGOperation_EJPP)(ExecState*, EncodedJSValue, EncodedJSValue, void*);
 typedef void DFG_OPERATION (*V_DFGOperation_EPZJ)(ExecState*, void*, int32_t, EncodedJSValue);
-typedef void DFG_OPERATION (*V_DFGOperation_EAZJ)(ExecState*, JSArray*, int32_t, EncodedJSValue);
-typedef double DFG_OPERATION (*D_DFGOperation_DD)(double, double);
-typedef double DFG_OPERATION (*D_DFGOperation_EJ)(ExecState*, EncodedJSValue);
-typedef void* DFG_OPERATION (*P_DFGOperation_E)(ExecState*);
 typedef void DFG_OPERATION (V_DFGOperation_EC)(ExecState*, JSCell*);
+typedef void* DFG_OPERATION (*P_DFGOperation_E)(ExecState*);
 
 // These routines are provide callbacks out to C++ implementations of operations too complex to JIT.
 JSCell* DFG_OPERATION operationNewObject(ExecState*);
@@ -123,6 +125,7 @@ void DFG_OPERATION operationPutByValBeyondArrayBoundsStrict(ExecState*, JSArray*
 void DFG_OPERATION operationPutByValBeyondArrayBoundsNonStrict(ExecState*, JSArray*, int32_t index, EncodedJSValue encodedValue);
 EncodedJSValue DFG_OPERATION operationArrayPush(ExecState*, EncodedJSValue encodedValue, JSArray*);
 EncodedJSValue DFG_OPERATION operationArrayPop(ExecState*, JSArray*);
+EncodedJSValue DFG_OPERATION operationRegExpExec(ExecState*, JSCell*, JSCell*);
 void DFG_OPERATION operationPutByIdStrict(ExecState*, EncodedJSValue encodedValue, JSCell* base, Identifier*);
 void DFG_OPERATION operationPutByIdNonStrict(ExecState*, EncodedJSValue encodedValue, JSCell* base, Identifier*);
 void DFG_OPERATION operationPutByIdDirectStrict(ExecState*, EncodedJSValue encodedValue, JSCell* base, Identifier*);
@@ -136,6 +139,7 @@ void DFG_OPERATION operationPutByIdNonStrictBuildList(ExecState*, EncodedJSValue
 void DFG_OPERATION operationPutByIdDirectStrictBuildList(ExecState*, EncodedJSValue encodedValue, JSCell* base, Identifier*);
 void DFG_OPERATION operationPutByIdDirectNonStrictBuildList(ExecState*, EncodedJSValue encodedValue, JSCell* base, Identifier*);
 // These comparisons return a boolean within a size_t such that the value is zero extended to fill the register.
+size_t DFG_OPERATION operationRegExpTest(ExecState*, JSCell*, JSCell*);
 size_t DFG_OPERATION operationCompareLess(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
 size_t DFG_OPERATION operationCompareLessEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
 size_t DFG_OPERATION operationCompareGreater(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
index 2e42929..4e68a94 100644 (file)
@@ -186,6 +186,14 @@ private:
             break;
         }
 
+        case RegExpExec:
+        case RegExpTest: {
+            if (node.getHeapPrediction())
+                changed |= mergePrediction(node.getHeapPrediction());
+            changed |= mergeDefaultArithFlags(node, flags);
+            break;
+        }
+
         case StringCharCodeAt: {
             changed |= mergePrediction(PredictInt32);
             changed |= mergeDefaultArithFlags(node, flags);
index 90c7171..d702707 100644 (file)
@@ -2764,6 +2764,46 @@ void SpeculativeJIT::compileNewFunctionExpression(Node& node)
     cellResult(resultGPR, m_compileIndex);
 }
 
+bool SpeculativeJIT::compileRegExpExec(Node& node)
+{
+    unsigned branchIndexInBlock = detectPeepHoleBranch();
+    if (branchIndexInBlock == UINT_MAX)
+        return false;
+    NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock);
+    ASSERT(node.adjustedRefCount() == 1);
+
+    Node& branchNode = at(branchNodeIndex);
+    BlockIndex taken = branchNode.takenBlockIndex();
+    BlockIndex notTaken = branchNode.notTakenBlockIndex();
+    
+    bool invert = false;
+    if (taken == (m_block + 1)) {
+        invert = true;
+        BlockIndex tmp = taken;
+        taken = notTaken;
+        notTaken = tmp;
+    }
+
+    SpeculateCellOperand base(this, node.child1());
+    SpeculateCellOperand argument(this, node.child2());
+    GPRReg baseGPR = base.gpr();
+    GPRReg argumentGPR = argument.gpr();
+    
+    flushRegisters();
+    GPRResult result(this);
+    callOperation(operationRegExpTest, result.gpr(), baseGPR, argumentGPR);
+
+    branchTest32(invert ? JITCompiler::Zero : JITCompiler::NonZero, result.gpr(), taken);
+    jump(notTaken);
+
+    use(node.child1());
+    use(node.child2());
+    m_indexInBlock = branchIndexInBlock;
+    m_compileIndex = branchNodeIndex;
+
+    return true;
+}
+
 } } // namespace JSC::DFG
 
 #endif
index f207d68..e93f3b3 100644 (file)
@@ -1184,6 +1184,11 @@ private:
         m_jit.setupArgumentsWithExecState(arg1, arg2);
         return appendCallWithExceptionCheckSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(S_DFGOperation_ECC operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2);
+        return appendCallWithExceptionCheckSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(J_DFGOperation_EPP operation, GPRReg result, GPRReg arg1, GPRReg arg2)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -1199,6 +1204,11 @@ private:
         m_jit.setupArgumentsWithExecState(MacroAssembler::TrustedImmPtr(static_cast<const void*>(JSValue::encode(jsNumber(imm.m_value)))), arg2);
         return appendCallWithExceptionCheckSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(J_DFGOperation_ECC operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2);
+        return appendCallWithExceptionCheckSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(J_DFGOperation_ECJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -1357,6 +1367,11 @@ private:
         m_jit.setupArgumentsWithExecState(arg1Payload, arg1Tag);
         return appendCallWithExceptionCheckSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(S_DFGOperation_ECC operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2);
+        return appendCallWithExceptionCheckSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(S_DFGOperation_EJJ operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2Tag, GPRReg arg2Payload)
     {
         m_jit.setupArgumentsWithExecState(arg1Payload, arg1Tag, arg2Payload, arg2Tag);
@@ -1382,6 +1397,11 @@ private:
         m_jit.setupArgumentsWithExecState(arg1, arg2Payload, arg2Tag);
         return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag);
     }
+    JITCompiler::Call callOperation(J_DFGOperation_ECC operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1, GPRReg arg2)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2);
+        return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag);
+    }
     JITCompiler::Call callOperation(V_DFGOperation_EC operation, GPRReg arg1)
     {
         m_jit.setupArgumentsWithExecState(arg1);
@@ -1738,8 +1758,9 @@ private:
     void compilePutByValForIntTypedArray(const TypedArrayDescriptor&, GPRReg base, GPRReg property, Node&, size_t elementSize, TypedArraySpeculationRequirements, TypedArraySignedness, TypedArrayRounding = TruncateRounding);
     void compileGetByValOnFloatTypedArray(const TypedArrayDescriptor&, Node&, size_t elementSize, TypedArraySpeculationRequirements);
     void compilePutByValForFloatTypedArray(const TypedArrayDescriptor&, GPRReg base, GPRReg property, Node&, size_t elementSize, TypedArraySpeculationRequirements);
-    void compileNewFunctionNoCheck(Node& node);
-    void compileNewFunctionExpression(Node& node);
+    void compileNewFunctionNoCheck(Node&);
+    void compileNewFunctionExpression(Node&);
+    bool compileRegExpExec(Node&);
     
     template <typename ClassType, bool destructor, typename StructureType> 
     void emitAllocateBasicJSObject(StructureType structure, GPRReg resultGPR, GPRReg scratchGPR, MacroAssembler::JumpList& slowPath)
index fb6ffa7..510e247 100644 (file)
@@ -2515,6 +2515,53 @@ void SpeculativeJIT::compile(Node& node)
         break;
     }
 
+    case RegExpExec: {
+        if (compileRegExpExec(node))
+            return;
+
+        if (!node.adjustedRefCount()) {
+            SpeculateCellOperand base(this, node.child1());
+            SpeculateCellOperand argument(this, node.child2());
+            GPRReg baseGPR = base.gpr();
+            GPRReg argumentGPR = argument.gpr();
+            
+            flushRegisters();
+            GPRResult result(this);
+            callOperation(operationRegExpTest, result.gpr(), baseGPR, argumentGPR);
+            
+            noResult(m_compileIndex);
+            break;
+        }
+
+        SpeculateCellOperand base(this, node.child1());
+        SpeculateCellOperand argument(this, node.child2());
+        GPRReg baseGPR = base.gpr();
+        GPRReg argumentGPR = argument.gpr();
+        
+        flushRegisters();
+        GPRResult2 resultTag(this);
+        GPRResult resultPayload(this);
+        callOperation(operationRegExpExec, resultTag.gpr(), resultPayload.gpr(), baseGPR, argumentGPR);
+        
+        jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex);
+        break;
+    }
+        
+    case RegExpTest: {
+        SpeculateCellOperand base(this, node.child1());
+        SpeculateCellOperand argument(this, node.child2());
+        GPRReg baseGPR = base.gpr();
+        GPRReg argumentGPR = argument.gpr();
+        
+        flushRegisters();
+        GPRResult result(this);
+        callOperation(operationRegExpTest, result.gpr(), baseGPR, argumentGPR);
+        
+        // If we add a DataFormatBool, we should use it here.
+        booleanResult(result.gpr(), m_compileIndex);
+        break;
+    }
+        
     case ArrayPush: {
         SpeculateCellOperand base(this, node.child1());
         JSValueOperand value(this, node.child2());
index 2dba0fc..aa38f9a 100644 (file)
@@ -2559,6 +2559,53 @@ void SpeculativeJIT::compile(Node& node)
         break;
     }
         
+    case RegExpExec: {
+        if (compileRegExpExec(node))
+            return;
+
+        if (!node.adjustedRefCount()) {
+            SpeculateCellOperand base(this, node.child1());
+            SpeculateCellOperand argument(this, node.child2());
+            GPRReg baseGPR = base.gpr();
+            GPRReg argumentGPR = argument.gpr();
+            
+            flushRegisters();
+            GPRResult result(this);
+            callOperation(operationRegExpTest, result.gpr(), baseGPR, argumentGPR);
+            
+            noResult(m_compileIndex);
+            break;
+        }
+
+        SpeculateCellOperand base(this, node.child1());
+        SpeculateCellOperand argument(this, node.child2());
+        GPRReg baseGPR = base.gpr();
+        GPRReg argumentGPR = argument.gpr();
+        
+        flushRegisters();
+        GPRResult result(this);
+        callOperation(operationRegExpExec, result.gpr(), baseGPR, argumentGPR);
+        
+        jsValueResult(result.gpr(), m_compileIndex);
+        break;
+    }
+
+    case RegExpTest: {
+        SpeculateCellOperand base(this, node.child1());
+        SpeculateCellOperand argument(this, node.child2());
+        GPRReg baseGPR = base.gpr();
+        GPRReg argumentGPR = argument.gpr();
+        
+        flushRegisters();
+        GPRResult result(this);
+        callOperation(operationRegExpTest, result.gpr(), baseGPR, argumentGPR);
+        
+        // If we add a DataFormatBool, we should use it here.
+        m_jit.or32(TrustedImm32(ValueFalse), result.gpr());
+        jsValueResult(result.gpr(), m_compileIndex, DataFormatJSBoolean);
+        break;
+    }
+        
     case ArrayPush: {
         SpeculateCellOperand base(this, node.child1());
         JSValueOperand value(this, node.child2());
index 3d9d1d3..4d27a80 100644 (file)
@@ -221,7 +221,7 @@ protected:
     void addConstructableFunction(JSGlobalData& globalData, const char* name, NativeFunction function, unsigned arguments)
     {
         Identifier identifier(globalExec(), name);
-        putDirect(globalData, identifier, JSFunction::create(globalExec(), this, arguments, identifier, function, function));
+        putDirect(globalData, identifier, JSFunction::create(globalExec(), this, arguments, identifier, function, NoIntrinsic, function));
     }
 };
 COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
index 5cc0068..73244e7 100644 (file)
@@ -45,6 +45,8 @@ enum Intrinsic {
     RoundIntrinsic,
     ExpIntrinsic,
     LogIntrinsic,
+    RegExpExecIntrinsic,
+    RegExpTestIntrinsic,
 };
 
 } // namespace JSC
index fa798f4..a588551 100644 (file)
@@ -59,22 +59,23 @@ bool JSFunction::isHostFunctionNonInline() const
     return isHostFunction();
 }
 
-JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, int length, const Identifier& name, NativeFunction nativeFunction, NativeFunction nativeConstructor)
+JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, int length, const Identifier& name, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor)
 {
-    NativeExecutable* executable = exec->globalData().getHostFunction(nativeFunction, nativeConstructor);
+    NativeExecutable* executable;
+#if ENABLE(JIT)
+    if (intrinsic != NoIntrinsic && exec->globalData().canUseJIT()) {
+        ASSERT(nativeConstructor == callHostFunctionAsConstructor);
+        executable = exec->globalData().getHostFunction(nativeFunction, intrinsic);
+    } else
+#endif
+        executable = exec->globalData().getHostFunction(nativeFunction, nativeConstructor);
+
     JSFunction* function = new (NotNull, allocateCell<JSFunction>(*exec->heap())) JSFunction(exec, globalObject, globalObject->functionStructure());
     // Can't do this during initialization because getHostFunction might do a GC allocation.
     function->finishCreation(exec, executable, length, name);
     return function;
 }
 
-JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, int length, const Identifier& name, NativeExecutable* nativeExecutable)
-{
-    JSFunction* function = new (NotNull, allocateCell<JSFunction>(*exec->heap())) JSFunction(exec, globalObject, globalObject->functionStructure());
-    function->finishCreation(exec, nativeExecutable, length, name);
-    return function;
-}
-
 JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure)
     : Base(exec->globalData(), structure)
     , m_executable()
index 2881810..9fb12ed 100644 (file)
@@ -54,8 +54,7 @@ namespace JSC {
     public:
         typedef JSNonFinalObject Base;
 
-        JS_EXPORT_PRIVATE static JSFunction* create(ExecState*, JSGlobalObject*, int length, const Identifier& name, NativeFunction nativeFunction, NativeFunction nativeConstructor = callHostFunctionAsConstructor);
-        static JSFunction* create(ExecState*, JSGlobalObject*, int length, const Identifier& name, NativeExecutable* nativeExecutable);
+        JS_EXPORT_PRIVATE static JSFunction* create(ExecState*, JSGlobalObject*, int length, const Identifier& name, NativeFunction nativeFunction, Intrinsic = NoIntrinsic, NativeFunction nativeConstructor = callHostFunctionAsConstructor);
 
         static JSFunction* create(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChain)
         {
index 55c048f..b935eb2 100644 (file)
@@ -76,15 +76,7 @@ bool setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject*
         if (thisObj->staticFunctionsReified())
             return false;
     
-        JSFunction* function;
-        JSGlobalObject* globalObject = thisObj->globalObject();
-#if ENABLE(JIT)
-        if (exec->globalData().canUseJIT() && entry->intrinsic() != NoIntrinsic)
-            function = JSFunction::create(exec, globalObject, entry->functionLength(), propertyName, exec->globalData().getHostFunction(entry->function(), entry->intrinsic()));
-        else
-#endif
-            function = JSFunction::create(exec, globalObject, entry->functionLength(), propertyName, entry->function());
-
+        JSFunction* function = JSFunction::create(exec, thisObj->globalObject(), entry->functionLength(), propertyName, entry->function(), entry->intrinsic());
         thisObj->putDirect(exec->globalData(), propertyName, function, entry->attributes());
         location = thisObj->getDirectLocation(exec->globalData(), propertyName);
     }
index a81799c..d6f2d6e 100644 (file)
@@ -276,23 +276,18 @@ void RegExpObject::put(JSCell* cell, ExecState* exec, const Identifier& property
     lookupPut<RegExpObject, JSObject>(exec, propertyName, value, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), slot);
 }
 
-JSValue RegExpObject::test(ExecState* exec)
+JSValue RegExpObject::exec(ExecState* exec, JSString* string)
 {
-    return jsBoolean(match(exec));
-}
-
-JSValue RegExpObject::exec(ExecState* exec)
-{
-    if (match(exec))
+    if (match(exec, string))
         return exec->lexicalGlobalObject()->regExpConstructor()->arrayOfMatches(exec);
     return jsNull();
 }
 
 // Shared implementation used by test and exec.
-bool RegExpObject::match(ExecState* exec)
+bool RegExpObject::match(ExecState* exec, JSString* string)
 {
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
-    UString input = exec->argument(0).toString(exec)->value(exec);
+    UString input = string->value(exec);
     JSGlobalData* globalData = &exec->globalData();
     if (!regExp()->global()) {
         int position;
index 456cfa6..7130d2f 100644 (file)
@@ -67,8 +67,8 @@ namespace JSC {
             return m_lastIndex.get();
         }
 
-        JSValue test(ExecState*);
-        JSValue exec(ExecState*);
+        bool match(ExecState*, JSString* string);
+        JSValue exec(ExecState*, JSString* string);
 
         static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&);
         static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&);
@@ -95,8 +95,6 @@ namespace JSC {
         JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow);
 
     private:
-        bool match(ExecState*);
-
         WriteBarrier<RegExp> m_regExp;
         WriteBarrier<Unknown> m_lastIndex;
         bool m_lastIndexIsWritable;
index 8e4b5a9..ebf5402 100644 (file)
@@ -84,7 +84,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec)
     JSValue thisValue = exec->hostThisValue();
     if (!thisValue.inherits(&RegExpObject::s_info))
         return throwVMTypeError(exec);
-    return JSValue::encode(asRegExpObject(thisValue)->test(exec));
+    return JSValue::encode(jsBoolean(asRegExpObject(thisValue)->match(exec, exec->argument(0).toString(exec))));
 }
 
 EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec)
@@ -92,7 +92,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec)
     JSValue thisValue = exec->hostThisValue();
     if (!thisValue.inherits(&RegExpObject::s_info))
         return throwVMTypeError(exec);
-    return JSValue::encode(asRegExpObject(thisValue)->exec(exec));
+    return JSValue::encode(asRegExpObject(thisValue)->exec(exec, exec->argument(0).toString(exec)));
 }
 
 EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec)