DFG/FTL should inline accesses to RegExpObject::m_lastIndex
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Mar 2016 06:36:24 +0000 (06:36 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Mar 2016 06:36:24 +0000 (06:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=155003

Reviewed by Benjamin Poulain.

Source/JavaScriptCore:

The Octane/regexp benchmark sets RegExps' lastIndex a lot. I could imagine this being
something that people want to do. Right now, I'm not convinced that making the RegExp object
be more plain-JS would be a good idea considering that pretty much all uses of it will
require some special compiler magic. Also, it's good that this patch teaches the compiler
how to reason about lastIndex since some of my other plans for regexp involve having the
compiler treat more regexp stuff as intrinsic.

This is a smaller Octane/regexp speed-up than I hoped - maybe around 1%. It's an enormous
speed-up on the microbenchmarks attached to this patch.

* dfg/DFGAbstractHeap.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGHeapLocation.h:
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compilePutAccessorByVal):
(JSC::DFG::SpeculativeJIT::compileGetRegExpObjectLastIndex):
(JSC::DFG::SpeculativeJIT::compileSetRegExpObjectLastIndex):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStoreBarrierInsertionPhase.cpp:
* ftl/FTLAbstractHeapRepository.cpp:
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileStringReplace):
(JSC::FTL::DFG::LowerDFGToB3::compileGetRegExpObjectLastIndex):
(JSC::FTL::DFG::LowerDFGToB3::compileSetRegExpObjectLastIndex):
(JSC::FTL::DFG::LowerDFGToB3::didOverflowStack):
(JSC::FTL::DFG::LowerDFGToB3::lowObject):
(JSC::FTL::DFG::LowerDFGToB3::lowRegExpObject):
(JSC::FTL::DFG::LowerDFGToB3::lowString):
* runtime/RegExpObject.h:
(JSC::RegExpObject::createStructure):
(JSC::RegExpObject::offsetOfLastIndex):

LayoutTests:

* js/regress/regexp-last-index-expected.txt: Added.
* js/regress/regexp-last-index.html: Added.
* js/regress/regexp-set-last-index-expected.txt: Added.
* js/regress/regexp-set-last-index.html: Added.
* js/regress/script-tests/regexp-last-index.js: Added.
* js/regress/script-tests/regexp-set-last-index.js: Added.

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

28 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/regexp-last-index-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/regexp-last-index.html [new file with mode: 0644]
LayoutTests/js/regress/regexp-set-last-index-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/regexp-set-last-index.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/regexp-last-index.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/regexp-set-last-index.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractHeap.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
Source/JavaScriptCore/dfg/DFGHeapLocation.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/RegExpObject.h

index 1f651be..ce83de6 100644 (file)
@@ -1,5 +1,19 @@
 2016-03-03  Filip Pizlo  <fpizlo@apple.com>
 
+        DFG/FTL should inline accesses to RegExpObject::m_lastIndex
+        https://bugs.webkit.org/show_bug.cgi?id=155003
+
+        Reviewed by Benjamin Poulain.
+
+        * js/regress/regexp-last-index-expected.txt: Added.
+        * js/regress/regexp-last-index.html: Added.
+        * js/regress/regexp-set-last-index-expected.txt: Added.
+        * js/regress/regexp-set-last-index.html: Added.
+        * js/regress/script-tests/regexp-last-index.js: Added.
+        * js/regress/script-tests/regexp-set-last-index.js: Added.
+
+2016-03-03  Filip Pizlo  <fpizlo@apple.com>
+
         Unreviewed, remove test with flaky timeout that doesn't test anything anymore. This test
         was relevant back when arguments got torn off; something that we don't do anymore. It
         uses a loop that waits for the top-tier compiler to compile it. We don't write tests
diff --git a/LayoutTests/js/regress/regexp-last-index-expected.txt b/LayoutTests/js/regress/regexp-last-index-expected.txt
new file mode 100644 (file)
index 0000000..9b40af4
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/regexp-last-index
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/regexp-last-index.html b/LayoutTests/js/regress/regexp-last-index.html
new file mode 100644 (file)
index 0000000..d41b73b
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/regexp-last-index.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/regexp-set-last-index-expected.txt b/LayoutTests/js/regress/regexp-set-last-index-expected.txt
new file mode 100644 (file)
index 0000000..f62edf2
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/regexp-set-last-index
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/regexp-set-last-index.html b/LayoutTests/js/regress/regexp-set-last-index.html
new file mode 100644 (file)
index 0000000..0760561
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/regexp-set-last-index.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/regexp-last-index.js b/LayoutTests/js/regress/script-tests/regexp-last-index.js
new file mode 100644 (file)
index 0000000..977b92d
--- /dev/null
@@ -0,0 +1,10 @@
+(function() {
+    var re = /foo/g;
+    re.exec("bar foo bar");
+    var result = 0;
+    var n = 10000000;
+    for (var i = 0; i < n; ++i)
+        result += re.lastIndex;
+    if (result != 7 * n)
+        throw "Error: bad result: " + result;
+})();
diff --git a/LayoutTests/js/regress/script-tests/regexp-set-last-index.js b/LayoutTests/js/regress/script-tests/regexp-set-last-index.js
new file mode 100644 (file)
index 0000000..1668584
--- /dev/null
@@ -0,0 +1,9 @@
+(function() {
+    var re = /foo/g;
+    var n = 10000000;
+    for (var i = 0; i < n; ++i)
+        re.lastIndex = 5;
+    re.exec("foo bar foo");
+    if (re.lastIndex != "foo bar foo".length)
+        throw "Error: bad value of lastIndx: " + re.lastIndex;
+})();
index 5225323..aaf1924 100644 (file)
@@ -1,3 +1,62 @@
+2016-03-03  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG/FTL should inline accesses to RegExpObject::m_lastIndex
+        https://bugs.webkit.org/show_bug.cgi?id=155003
+
+        Reviewed by Benjamin Poulain.
+
+        The Octane/regexp benchmark sets RegExps' lastIndex a lot. I could imagine this being
+        something that people want to do. Right now, I'm not convinced that making the RegExp object
+        be more plain-JS would be a good idea considering that pretty much all uses of it will
+        require some special compiler magic. Also, it's good that this patch teaches the compiler
+        how to reason about lastIndex since some of my other plans for regexp involve having the
+        compiler treat more regexp stuff as intrinsic.
+
+        This is a smaller Octane/regexp speed-up than I hoped - maybe around 1%. It's an enormous
+        speed-up on the microbenchmarks attached to this patch.
+
+        * dfg/DFGAbstractHeap.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGHeapLocation.h:
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compilePutAccessorByVal):
+        (JSC::DFG::SpeculativeJIT::compileGetRegExpObjectLastIndex):
+        (JSC::DFG::SpeculativeJIT::compileSetRegExpObjectLastIndex):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStoreBarrierInsertionPhase.cpp:
+        * ftl/FTLAbstractHeapRepository.cpp:
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileStringReplace):
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetRegExpObjectLastIndex):
+        (JSC::FTL::DFG::LowerDFGToB3::compileSetRegExpObjectLastIndex):
+        (JSC::FTL::DFG::LowerDFGToB3::didOverflowStack):
+        (JSC::FTL::DFG::LowerDFGToB3::lowObject):
+        (JSC::FTL::DFG::LowerDFGToB3::lowRegExpObject):
+        (JSC::FTL::DFG::LowerDFGToB3::lowString):
+        * runtime/RegExpObject.h:
+        (JSC::RegExpObject::createStructure):
+        (JSC::RegExpObject::offsetOfLastIndex):
+
 2016-03-03  Chris Dumez  <cdumez@apple.com>
 
         Regression(r196770): Unable to use HipChat Mac app
index 4dec8fa..afe10c4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -58,6 +58,7 @@ namespace JSC { namespace DFG {
     macro(JSCell_typeInfoType) \
     macro(JSObject_butterfly) \
     macro(JSPropertyNameEnumerator_cachedPropertyNames) \
+    macro(RegExpObject_lastIndex) \
     macro(NamedProperties) \
     macro(IndexedInt32Properties) \
     macro(IndexedDoubleProperties) \
index 338a903..1b4c7f0 100644 (file)
@@ -1882,6 +1882,13 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             
     case PutClosureVar:
         break;
+
+    case GetRegExpObjectLastIndex:
+        forNode(node).makeHeapTop();
+        break;
+
+    case SetRegExpObjectLastIndex:
+        break;
         
     case GetFromArguments:
         forNode(node).makeHeapTop();
index 4612177..cc33229 100644 (file)
@@ -908,6 +908,16 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         write(AbstractHeap(ScopeProperties, node->scopeOffset().offset()));
         def(HeapLocation(ClosureVariableLoc, AbstractHeap(ScopeProperties, node->scopeOffset().offset()), node->child1()), LazyNode(node->child2().node()));
         return;
+
+    case GetRegExpObjectLastIndex:
+        read(RegExpObject_lastIndex);
+        def(HeapLocation(RegExpObjectLastIndexLoc, RegExpObject_lastIndex, node->child1()), LazyNode(node));
+        return;
+
+    case SetRegExpObjectLastIndex:
+        write(RegExpObject_lastIndex);
+        def(HeapLocation(RegExpObjectLastIndexLoc, RegExpObject_lastIndex, node->child1()), LazyNode(node->child2().node()));
+        return;
         
     case GetFromArguments: {
         AbstractHeap heap(DirectArgumentsProperties, node->capturedArgumentsOffset().offset());
index eec32fe..7004323 100644 (file)
@@ -113,6 +113,8 @@ bool doesGC(Graph& graph, Node* node)
     case SkipScope:
     case GetClosureVar:
     case PutClosureVar:
+    case GetRegExpObjectLastIndex:
+    case SetRegExpObjectLastIndex:
     case GetGlobalVar:
     case GetGlobalLexicalVariable:
     case PutGlobalVariable:
index fe495e2..cf515d1 100644 (file)
@@ -1072,11 +1072,21 @@ private:
                 && !m_graph.hasExitSite(node->origin.semantic, BadCache)
                 && !m_graph.hasExitSite(node->origin.semantic, BadIndexingType)
                 && !m_graph.hasExitSite(node->origin.semantic, ExoticObjectMode)) {
+                
                 auto uid = m_graph.identifiers()[node->identifierNumber()];
+                
                 if (uid == vm().propertyNames->length.impl()) {
                     attemptToMakeGetArrayLength(node);
                     break;
                 }
+
+                if (uid == vm().propertyNames->lastIndex.impl()
+                    && node->child1()->shouldSpeculateRegExpObject()) {
+                    node->setOp(GetRegExpObjectLastIndex);
+                    node->clearFlags(NodeMustGenerate);
+                    fixEdge<RegExpObjectUse>(node->child1());
+                    break;
+                }
             }
 
             if (node->child1()->shouldSpeculateCell())
@@ -1087,6 +1097,23 @@ private:
         case PutById:
         case PutByIdFlush:
         case PutByIdDirect: {
+            if (node->child1()->shouldSpeculateCellOrOther()
+                && !m_graph.hasExitSite(node->origin.semantic, BadType)
+                && !m_graph.hasExitSite(node->origin.semantic, BadCache)
+                && !m_graph.hasExitSite(node->origin.semantic, BadIndexingType)
+                && !m_graph.hasExitSite(node->origin.semantic, ExoticObjectMode)) {
+                
+                auto uid = m_graph.identifiers()[node->identifierNumber()];
+                
+                if (uid == vm().propertyNames->lastIndex.impl()
+                    && node->child1()->shouldSpeculateRegExpObject()) {
+                    node->setOp(SetRegExpObjectLastIndex);
+                    fixEdge<RegExpObjectUse>(node->child1());
+                    speculateForBarrier(node->child2());
+                    break;
+                }
+            }
+            
             fixEdge<CellUse>(node->child1());
             break;
         }
@@ -1267,6 +1294,8 @@ private:
         case KillStack:
         case GetStack:
         case StoreBarrier:
+        case GetRegExpObjectLastIndex:
+        case SetRegExpObjectLastIndex:
             // These are just nodes that we don't currently expect to see during fixup.
             // If we ever wanted to insert them prior to fixup, then we just have to create
             // fixup rules for them.
index 55d84cf..c807f99 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -147,6 +147,10 @@ void printInternal(PrintStream& out, LocationKind kind)
     case StructureLoc:
         out.print("StructureLoc");
         return;
+
+    case RegExpObjectLastIndexLoc:
+        out.print("RegExpObjectLastIndexLoc");
+        return;
     }
     
     RELEASE_ASSERT_NOT_REACHED();
index b85fa26..129c614 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -54,6 +54,7 @@ enum LocationKind {
     IsFunctionLoc,
     IsObjectOrNullLoc,
     NamedPropertyLoc,
+    RegExpObjectLastIndexLoc,
     SetterLoc,
     StructureLoc,
     TypedArrayByteOffsetLoc,
index 4035dd9..5200c4d 100644 (file)
@@ -219,6 +219,8 @@ namespace JSC { namespace DFG {
     macro(PutGlobalVariable, NodeMustGenerate) \
     macro(NotifyWrite, NodeMustGenerate) \
     macro(VarInjectionWatchpoint, NodeMustGenerate) \
+    macro(GetRegExpObjectLastIndex, NodeResultJS) \
+    macro(SetRegExpObjectLastIndex, NodeMustGenerate) \
     macro(CheckCell, NodeMustGenerate) \
     macro(CheckNotEmpty, NodeMustGenerate) \
     macro(CheckBadCell, NodeMustGenerate) \
index 9283ae3..666ad62 100644 (file)
@@ -665,7 +665,9 @@ private:
         case PutStack:
         case KillStack:
         case StoreBarrier:
-        case GetStack: {
+        case GetStack:
+        case GetRegExpObjectLastIndex:
+        case SetRegExpObjectLastIndex: {
             // This node should never be visible at this stage of compilation. It is
             // inserted by fixup(), which follows this phase.
             DFG_CRASH(m_graph, node, "Unexpected node during prediction propagation");
index 1924978..b8735bb 100644 (file)
@@ -332,6 +332,8 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case ForwardVarargs:
     case CopyRest:
     case StringReplace:
+    case GetRegExpObjectLastIndex:
+    case SetRegExpObjectLastIndex:
         return true;
 
     case BottomValue:
index 8ead192..6c24942 100644 (file)
@@ -7518,6 +7518,33 @@ void SpeculativeJIT::compilePutAccessorByVal(Node* node)
     noResult(node);
 }
 
+void SpeculativeJIT::compileGetRegExpObjectLastIndex(Node* node)
+{
+    SpeculateCellOperand regExp(this, node->child1());
+    JSValueRegsTemporary result(this);
+    GPRReg regExpGPR = regExp.gpr();
+    JSValueRegs resultRegs = result.regs();
+    speculateRegExpObject(node->child1(), regExpGPR);
+    m_jit.loadValue(JITCompiler::Address(regExpGPR, RegExpObject::offsetOfLastIndex()), resultRegs);
+    jsValueResult(resultRegs, node);
+}
+
+void SpeculativeJIT::compileSetRegExpObjectLastIndex(Node* node)
+{
+    SpeculateCellOperand regExp(this, node->child1());
+    JSValueOperand value(this, node->child2());
+    GPRReg regExpGPR = regExp.gpr();
+    JSValueRegs valueRegs = value.jsValueRegs();
+    speculateRegExpObject(node->child1(), regExpGPR);
+    speculationCheck(
+        ExoticObjectMode, JSValueRegs(), nullptr,
+        m_jit.branchTest8(
+            JITCompiler::Zero,
+            JITCompiler::Address(regExpGPR, RegExpObject::offsetOfLastIndexIsWritable())));
+    m_jit.storeValue(valueRegs, JITCompiler::Address(regExpGPR, RegExpObject::offsetOfLastIndex()));
+    noResult(node);
+}
+
 } } // namespace JSC::DFG
 
 #endif
index 8f2c454..31355a9 100644 (file)
@@ -2333,6 +2333,8 @@ public:
     void compilePutAccessorById(Node*);
     void compilePutGetterSetterById(Node*);
     void compilePutAccessorByVal(Node*);
+    void compileGetRegExpObjectLastIndex(Node*);
+    void compileSetRegExpObjectLastIndex(Node*);
     
     void moveTrueTo(GPRReg);
     void moveFalseTo(GPRReg);
index d8d9c88..2491dca 100644 (file)
@@ -2970,6 +2970,16 @@ void SpeculativeJIT::compile(Node* node)
         cellResult(resultPayload.gpr(), node);
         break;
     }
+
+    case GetRegExpObjectLastIndex: {
+        compileGetRegExpObjectLastIndex(node);
+        break;
+    }
+        
+    case SetRegExpObjectLastIndex: {
+        compileSetRegExpObjectLastIndex(node);
+        break;
+    }
         
     case ArrayPush: {
         ASSERT(node->arrayMode().isJSArray());
index ed94b67..e2cb5a3 100644 (file)
@@ -3088,6 +3088,16 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
         
+    case GetRegExpObjectLastIndex: {
+        compileGetRegExpObjectLastIndex(node);
+        break;
+    }
+        
+    case SetRegExpObjectLastIndex: {
+        compileSetRegExpObjectLastIndex(node);
+        break;
+    }
+        
     case ArrayPush: {
         ASSERT(node->arrayMode().isJSArray());
         
index 72f2200..9559c5a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -260,6 +260,7 @@ private:
                 
             case PutClosureVar:
             case PutToArguments:
+            case SetRegExpObjectLastIndex:
             case MultiPutByOffset: {
                 considerBarrier(m_node->child1(), m_node->child2());
                 break;
index 04d7a68..970ede8 100644 (file)
@@ -39,6 +39,7 @@
 #include "JSPropertyNameEnumerator.h"
 #include "JSScope.h"
 #include "JSCInlines.h"
+#include "RegExpObject.h"
 #include "ScopedArguments.h"
 #include "ScopedArgumentsTable.h"
 
index 9088455..6fcbb1b 100644 (file)
@@ -77,6 +77,8 @@ namespace JSC { namespace FTL {
     macro(JSSymbolTableObject_symbolTable, JSSymbolTableObject::offsetOfSymbolTable()) \
     macro(JSWrapperObject_internalValue, JSWrapperObject::internalValueOffset()) \
     macro(MarkedAllocator_freeListHead, MarkedAllocator::offsetOfFreeListHead()) \
+    macro(RegExpObject_lastIndex, RegExpObject::offsetOfLastIndex()) \
+    macro(RegExpObject_lastIndexIsWritable, RegExpObject::offsetOfLastIndexIsWritable()) \
     macro(ScopedArguments_overrodeThings, ScopedArguments::offsetOfOverrodeThings()) \
     macro(ScopedArguments_scope, ScopedArguments::offsetOfScope()) \
     macro(ScopedArguments_table, ScopedArguments::offsetOfTable()) \
index e49b11e..d5371fa 100644 (file)
@@ -224,6 +224,8 @@ inline CapabilityLevel canCompile(Node* node)
     case RegExpTest:
     case NewRegexp:
     case StringReplace:
+    case GetRegExpObjectLastIndex:
+    case SetRegExpObjectLastIndex:
         // These are OK.
         break;
 
index 512f6da..fd2e95a 100644 (file)
@@ -926,6 +926,12 @@ private:
         case StringReplace:
             compileStringReplace();
             break;
+        case GetRegExpObjectLastIndex:
+            compileGetRegExpObjectLastIndex();
+            break;
+        case SetRegExpObjectLastIndex:
+            compileSetRegExpObjectLastIndex();
+            break;
 
         case PhantomLocal:
         case LoopHint:
@@ -6489,8 +6495,7 @@ private:
             if (JSString* replace = m_node->child3()->dynamicCastConstant<JSString*>()) {
                 if (!replace->length()) {
                     LValue string = lowString(m_node->child1());
-                    LValue regExp = lowCell(m_node->child2());
-                    speculateRegExpObject(m_node->child2(), regExp);
+                    LValue regExp = lowRegExpObject(m_node->child2());
 
                     LValue result = vmCall(
                         Int64, m_out.operation(operationStringProtoFuncReplaceRegExpEmptyStr),
@@ -6502,8 +6507,7 @@ private:
             }
             
             LValue string = lowString(m_node->child1());
-            LValue regExp = lowCell(m_node->child2());
-            speculateRegExpObject(m_node->child2(), regExp);
+            LValue regExp = lowRegExpObject(m_node->child2());
             LValue replace = lowString(m_node->child3());
 
             LValue result = vmCall(
@@ -6522,6 +6526,23 @@ private:
         setJSValue(result);
     }
 
+    void compileGetRegExpObjectLastIndex()
+    {
+        setJSValue(m_out.load64(lowRegExpObject(m_node->child1()), m_heaps.RegExpObject_lastIndex));
+    }
+
+    void compileSetRegExpObjectLastIndex()
+    {
+        LValue regExp = lowRegExpObject(m_node->child1());
+        LValue value = lowJSValue(m_node->child2());
+
+        speculate(
+            ExoticObjectMode, noValue(), nullptr,
+            m_out.isZero32(m_out.load8ZeroExt32(regExp, m_heaps.RegExpObject_lastIndexIsWritable)));
+        
+        m_out.store64(value, regExp, m_heaps.RegExpObject_lastIndex);
+    }
+
     LValue didOverflowStack()
     {
         // This does a very simple leaf function analysis. The invariant of FTL call
@@ -8925,6 +8946,13 @@ private:
         speculateObject(edge, result);
         return result;
     }
+
+    LValue lowRegExpObject(Edge edge)
+    {
+        LValue result = lowCell(edge);
+        speculateRegExpObject(edge, result);
+        return result;
+    }
     
     LValue lowString(Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
     {
index 4901de0..a626d29 100644 (file)
@@ -74,6 +74,16 @@ public:
         return Structure::create(vm, globalObject, prototype, TypeInfo(RegExpObjectType, StructureFlags), info());
     }
 
+    static ptrdiff_t offsetOfLastIndex()
+    {
+        return OBJECT_OFFSETOF(RegExpObject, m_lastIndex);
+    }
+
+    static ptrdiff_t offsetOfLastIndexIsWritable()
+    {
+        return OBJECT_OFFSETOF(RegExpObject, m_lastIndexIsWritable);
+    }
+
 protected:
     JS_EXPORT_PRIVATE RegExpObject(VM&, Structure*, RegExp*);
     JS_EXPORT_PRIVATE void finishCreation(VM&);