[ES6] Handle new_generator_func / new_generator_func_exp in DFG / FTL
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Dec 2015 09:10:09 +0000 (09:10 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Dec 2015 09:10:09 +0000 (09:10 +0000)
https://bugs.webkit.org/show_bug.cgi?id=152227

Reviewed by Saam Barati.

Source/JavaScriptCore:

This patch introduces new_generator_func / new_generator_func_exp into DFG and FTL.
We add a new DFG Node, NewGeneratorFunction. It will construct a function with GeneratorFunction's structure.
The structure of GeneratorFunction is different from one of Function because GeneratorFunction has the different __proto__.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGClobbersExitState.cpp:
(JSC::DFG::clobbersExitState):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGMayExit.cpp:
(JSC::DFG::mayExit):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToPhantomNewFunction):
(JSC::DFG::Node::hasCellOperand):
(JSC::DFG::Node::isFunctionAllocation):
* dfg/DFGNodeType.h:
* dfg/DFGObjectAllocationSinkingPhase.cpp:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileNewFunction):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStoreBarrierInsertionPhase.cpp:
* dfg/DFGStructureRegistrationPhase.cpp:
(JSC::DFG::StructureRegistrationPhase::run):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
(JSC::FTL::DFG::LowerDFGToLLVM::compileNewFunction):
* tests/stress/generator-function-create-optimized.js: Added.
(shouldBe):
(g):
(test.return.gen):
(test):
(test2.gen):
(test2):
* tests/stress/generator-function-declaration-sinking-no-double-allocate.js: Added.
(shouldBe):
(GeneratorFunctionPrototype):
(call):
(f):
(sink):
* tests/stress/generator-function-declaration-sinking-osrexit.js: Added.
(shouldBe):
(GeneratorFunctionPrototype):
(g):
(f):
(sink):
* tests/stress/generator-function-declaration-sinking-put.js: Added.
(shouldBe):
(GeneratorFunctionPrototype):
(g):
(f):
(sink):
* tests/stress/generator-function-expression-sinking-no-double-allocate.js: Added.
(shouldBe):
(GeneratorFunctionPrototype):
(call):
(f):
(sink):
* tests/stress/generator-function-expression-sinking-osrexit.js: Added.
(shouldBe):
(GeneratorFunctionPrototype):
(g):
(sink):
* tests/stress/generator-function-expression-sinking-put.js: Added.
(shouldBe):
(GeneratorFunctionPrototype):
(g):
(sink):

LayoutTests:

Make the test taking longer time.

* js/regress/script-tests/generator-function-create.js:
(test):

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

30 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/script-tests/generator-function-create.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGMayExit.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp
Source/JavaScriptCore/dfg/DFGStructureRegistrationPhase.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/tests/stress/generator-function-create-optimized.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/generator-function-declaration-sinking-no-double-allocate.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/generator-function-declaration-sinking-osrexit.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/generator-function-declaration-sinking-put.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/generator-function-expression-sinking-no-double-allocate.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/generator-function-expression-sinking-osrexit.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/generator-function-expression-sinking-put.js [new file with mode: 0644]

index 6c811da..4722ca1 100644 (file)
@@ -1,3 +1,15 @@
+2015-12-16  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Handle new_generator_func / new_generator_func_exp in DFG / FTL
+        https://bugs.webkit.org/show_bug.cgi?id=152227
+
+        Reviewed by Saam Barati.
+
+        Make the test taking longer time.
+
+        * js/regress/script-tests/generator-function-create.js:
+        (test):
+
 2015-12-16  Tomas Popela  <tpopela@redhat.com>
 
         [GTK] Correct the wrong expectation introduced in r194106
index d10c137..ae4eb9e 100644 (file)
@@ -7,6 +7,12 @@ function createGeneratorFunction()
     }
     return gen;
 }
-noInline(createGeneratorFunction);
+function test()
+{
+    for (var i = 0; i < 500; ++i)
+        createGeneratorFunction();
+}
+noInline(test);
+
 for (var i = 0; i < 1e4; ++i)
-    createGeneratorFunction();
+    test();
index cae98ad..98f32bc 100644 (file)
@@ -1,3 +1,96 @@
+2015-12-16  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Handle new_generator_func / new_generator_func_exp in DFG / FTL
+        https://bugs.webkit.org/show_bug.cgi?id=152227
+
+        Reviewed by Saam Barati.
+
+        This patch introduces new_generator_func / new_generator_func_exp into DFG and FTL.
+        We add a new DFG Node, NewGeneratorFunction. It will construct a function with GeneratorFunction's structure.
+        The structure of GeneratorFunction is different from one of Function because GeneratorFunction has the different __proto__.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGClobbersExitState.cpp:
+        (JSC::DFG::clobbersExitState):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGMayExit.cpp:
+        (JSC::DFG::mayExit):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::convertToPhantomNewFunction):
+        (JSC::DFG::Node::hasCellOperand):
+        (JSC::DFG::Node::isFunctionAllocation):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGObjectAllocationSinkingPhase.cpp:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileNewFunction):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStoreBarrierInsertionPhase.cpp:
+        * dfg/DFGStructureRegistrationPhase.cpp:
+        (JSC::DFG::StructureRegistrationPhase::run):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileNewFunction):
+        * tests/stress/generator-function-create-optimized.js: Added.
+        (shouldBe):
+        (g):
+        (test.return.gen):
+        (test):
+        (test2.gen):
+        (test2):
+        * tests/stress/generator-function-declaration-sinking-no-double-allocate.js: Added.
+        (shouldBe):
+        (GeneratorFunctionPrototype):
+        (call):
+        (f):
+        (sink):
+        * tests/stress/generator-function-declaration-sinking-osrexit.js: Added.
+        (shouldBe):
+        (GeneratorFunctionPrototype):
+        (g):
+        (f):
+        (sink):
+        * tests/stress/generator-function-declaration-sinking-put.js: Added.
+        (shouldBe):
+        (GeneratorFunctionPrototype):
+        (g):
+        (f):
+        (sink):
+        * tests/stress/generator-function-expression-sinking-no-double-allocate.js: Added.
+        (shouldBe):
+        (GeneratorFunctionPrototype):
+        (call):
+        (f):
+        (sink):
+        * tests/stress/generator-function-expression-sinking-osrexit.js: Added.
+        (shouldBe):
+        (GeneratorFunctionPrototype):
+        (g):
+        (sink):
+        * tests/stress/generator-function-expression-sinking-put.js: Added.
+        (shouldBe):
+        (GeneratorFunctionPrototype):
+        (g):
+        (sink):
+
 2015-12-15  Mark Lam  <mark.lam@apple.com>
 
         Gardening: fix broken 32-bit JSC tests.  Just need to assign a scratch register.
index 31b548b..fbaeca0 100644 (file)
@@ -1760,7 +1760,12 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         forNode(node).set(
             m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->arrowFunctionStructure());
         break;
-            
+
+    case NewGeneratorFunction:
+        forNode(node).set(
+            m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->generatorFunctionStructure());
+        break;
+
     case NewFunction:
         forNode(node).set(
             m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->functionStructure());
index 66193be..6ad2143 100644 (file)
@@ -4557,23 +4557,27 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_put_to_arguments);
         }
             
-        case op_new_func: {
+        case op_new_func:
+        case op_new_generator_func: {
             FunctionExecutable* decl = m_inlineStackTop->m_profiledBlock->functionDecl(currentInstruction[3].u.operand);
             FrozenValue* frozen = m_graph.freezeStrong(decl);
-            set(VirtualRegister(currentInstruction[1].u.operand),
-                addToGraph(NewFunction, OpInfo(frozen), get(VirtualRegister(currentInstruction[2].u.operand))));
+            NodeType op = (opcodeID == op_new_generator_func) ? NewGeneratorFunction : NewFunction;
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(op, OpInfo(frozen), get(VirtualRegister(currentInstruction[2].u.operand))));
+            static_assert(OPCODE_LENGTH(op_new_func) == OPCODE_LENGTH(op_new_generator_func), "The length of op_new_func should eqaual to one of op_new_generator_func");
             NEXT_OPCODE(op_new_func);
         }
 
         case op_new_func_exp:
+        case op_new_generator_func_exp:
         case op_new_arrow_func_exp: {
             FunctionExecutable* expr = m_inlineStackTop->m_profiledBlock->functionExpr(currentInstruction[3].u.operand);
             FrozenValue* frozen = m_graph.freezeStrong(expr);
-            set(VirtualRegister(currentInstruction[1].u.operand),
-                addToGraph(NewFunction, OpInfo(frozen), get(VirtualRegister(currentInstruction[2].u.operand))));
+            NodeType op = (opcodeID == op_new_generator_func_exp) ? NewGeneratorFunction : NewFunction;
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(op, OpInfo(frozen), get(VirtualRegister(currentInstruction[2].u.operand))));
             
-            if (opcodeID == op_new_func_exp) {
+            if (opcodeID == op_new_func_exp || opcodeID == op_new_generator_func_exp) {
                 // Curly braces are necessary
+                static_assert(OPCODE_LENGTH(op_new_func_exp) == OPCODE_LENGTH(op_new_generator_func_exp), "The length of op_new_func_exp should eqaual to one of op_new_generator_func_exp");
                 NEXT_OPCODE(op_new_func_exp);
             } else {
                 // Curly braces are necessary
index bb5b1d1..d1c9bce 100644 (file)
@@ -215,6 +215,8 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_to_index_string:
     case op_new_func:
     case op_new_func_exp:
+    case op_new_generator_func:
+    case op_new_generator_func_exp:
     case op_new_arrow_func_exp:
     case op_create_lexical_environment:
     case op_get_parent_scope:
index ba7bf72..b5d5d2a 100644 (file)
@@ -1033,6 +1033,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     
     case NewArrowFunction:
     case NewFunction:
+    case NewGeneratorFunction:
         if (node->castOperand<FunctionExecutable*>()->singletonFunction()->isStillValid())
             write(Watchpoint_fire);
         read(HeapObjectCount);
index 54dde1f..b69fc5f 100644 (file)
@@ -77,6 +77,7 @@ bool clobbersExitState(Graph& graph, Node* node)
 
     case NewArrowFunction:
     case NewFunction:
+    case NewGeneratorFunction:
         // Like above, but with the JSFunction allocation caveat.
         return node->castOperand<FunctionExecutable*>()->singletonFunction()->isStillValid();
 
index db4fc21..5eff2b0 100644 (file)
@@ -247,6 +247,7 @@ bool doesGC(Graph& graph, Node* node)
     case MakeRope:
     case NewArrowFunction:
     case NewFunction:
+    case NewGeneratorFunction:
     case NewTypedArray:
     case ThrowReferenceError:
     case GetPropertyEnumerator:
index 82fa48d..5c1d412 100644 (file)
@@ -1387,7 +1387,8 @@ private:
 
         case CreateScopedArguments:
         case CreateActivation:
-        case NewFunction: {
+        case NewFunction:
+        case NewGeneratorFunction: {
             fixEdge<CellUse>(node->child1());
             break;
         }
index d1e21ed..daa2933 100644 (file)
@@ -143,6 +143,7 @@ ExitMode mayExit(Graph& graph, Node* node)
     case MaterializeNewObject:
     case NewFunction:
     case NewArrowFunction:
+    case NewGeneratorFunction:
     case NewStringObject:
     case CreateActivation:
         result = ExitsForExceptions;
index 1b3083b..11eeba5 100644 (file)
@@ -585,7 +585,7 @@ struct Node {
 
     void convertToPhantomNewFunction()
     {
-        ASSERT(m_op == NewFunction || m_op == NewArrowFunction);
+        ASSERT(m_op == NewFunction || m_op == NewArrowFunction || m_op == NewGeneratorFunction);
         m_op = PhantomNewFunction;
         m_flags |= NodeMustGenerate;
         m_opInfo = 0;
@@ -1356,6 +1356,7 @@ struct Node {
         case CheckCell:
         case NewFunction:
         case NewArrowFunction:
+        case NewGeneratorFunction:
         case CreateActivation:
         case MaterializeCreateActivation:
             return true;
@@ -1574,6 +1575,7 @@ struct Node {
         switch (op()) {
         case NewArrowFunction:
         case NewFunction:
+        case NewGeneratorFunction:
             return true;
         default:
             return false;
index ac699e1..d4a9271 100644 (file)
@@ -312,6 +312,7 @@ namespace JSC { namespace DFG {
     macro(NewFunction, NodeResultJS) \
     \
     macro(NewArrowFunction, NodeResultJS) \
+    macro(NewGeneratorFunction, NodeResultJS) \
     \
     /* These aren't terminals but always exit */ \
     macro(Throw, NodeMustGenerate) \
index 12ab163..fbb0af2 100644 (file)
@@ -139,7 +139,7 @@ public:
     // once it is escaped if it still has pointers to it in order to
     // replace any use of those pointers by the corresponding
     // materialization
-    enum class Kind { Escaped, Object, Activation, Function, NewArrowFunction };
+    enum class Kind { Escaped, Object, Activation, Function, ArrowFunction, GeneratorFunction };
 
     explicit Allocation(Node* identifier = nullptr, Kind kind = Kind::Escaped)
         : m_identifier(identifier)
@@ -233,12 +233,7 @@ public:
 
     bool isFunctionAllocation() const
     {
-        return m_kind == Kind::Function || m_kind == Kind::NewArrowFunction;
-    }
-    
-    bool isArrowFunctionAllocation() const
-    {
-        return m_kind == Kind::NewArrowFunction;
+        return m_kind == Kind::Function || m_kind == Kind::ArrowFunction || m_kind == Kind::GeneratorFunction;
     }
 
     bool operator==(const Allocation& other) const
@@ -274,8 +269,12 @@ public:
             out.print("Function");
             break;
                 
-        case Kind::NewArrowFunction:
-            out.print("NewArrowFunction");
+        case Kind::ArrowFunction:
+            out.print("ArrowFunction");
+            break;
+
+        case Kind::GeneratorFunction:
+            out.print("GeneratorFunction");
             break;
 
         case Kind::Activation:
@@ -837,13 +836,19 @@ private:
             break;
 
         case NewFunction:
-        case NewArrowFunction: {
+        case NewArrowFunction:
+        case NewGeneratorFunction: {
             if (node->castOperand<FunctionExecutable*>()->singletonFunction()->isStillValid()) {
                 m_heap.escape(node->child1().node());
                 break;
             }
             
-            target = &m_heap.newAllocation(node, Allocation::Kind::Function);
+            if (node->op() == NewGeneratorFunction)
+                target = &m_heap.newAllocation(node, Allocation::Kind::GeneratorFunction);
+            else if (node->op() == NewArrowFunction)
+                target = &m_heap.newAllocation(node, Allocation::Kind::ArrowFunction);
+            else
+                target = &m_heap.newAllocation(node, Allocation::Kind::Function);
             writes.add(FunctionExecutablePLoc, LazyNode(node->cellOperand()));
             writes.add(FunctionActivationPLoc, LazyNode(node->child1().node()));
             break;
@@ -1446,11 +1451,14 @@ private:
                 OpInfo(set), OpInfo(data), 0, 0);
         }
 
-        case Allocation::Kind::NewArrowFunction:
+        case Allocation::Kind::ArrowFunction:
+        case Allocation::Kind::GeneratorFunction:
         case Allocation::Kind::Function: {
             FrozenValue* executable = allocation.identifier()->cellOperand();
             
-            NodeType nodeType = allocation.kind() == Allocation::Kind::NewArrowFunction ? NewArrowFunction : NewFunction;
+            NodeType nodeType =
+                allocation.kind() == Allocation::Kind::ArrowFunction ? NewArrowFunction :
+                allocation.kind() == Allocation::Kind::GeneratorFunction ? NewGeneratorFunction : NewFunction;
             
             return m_graph.addNode(
                 allocation.identifier()->prediction(), nodeType,
@@ -1782,6 +1790,7 @@ private:
 
                     case NewArrowFunction:
                     case NewFunction:
+                    case NewGeneratorFunction:
                         node->convertToPhantomNewFunction();
                         break;
 
@@ -2033,7 +2042,8 @@ private:
         }
         
         case NewFunction:
-        case NewArrowFunction: {
+        case NewArrowFunction:
+        case NewGeneratorFunction: {
             Vector<PromotedHeapLocation> locations = m_locationsForAllocation.get(escapee);
             ASSERT(locations.size() == 2);
                 
index 413bf89..6127716 100644 (file)
@@ -209,7 +209,8 @@ private:
         case GetSetter:
         case GetCallee:
         case NewArrowFunction:
-        case NewFunction: {
+        case NewFunction:
+        case NewGeneratorFunction: {
             changed |= setPrediction(SpecFunction);
             break;
         }
index 0f456c9..b28cfc5 100644 (file)
@@ -268,6 +268,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case PutToArguments:
     case NewArrowFunction:
     case NewFunction:
+    case NewGeneratorFunction:
     case Jump:
     case Branch:
     case Switch:
index f644220..02ff82d 100755 (executable)
@@ -50,6 +50,7 @@
 #include "JSArrowFunction.h"
 #include "JSCInlines.h"
 #include "JSEnvironmentRecord.h"
+#include "JSGeneratorFunction.h"
 #include "JSLexicalEnvironment.h"
 #include "LinkBuffer.h"
 #include "ScopedArguments.h"
@@ -5372,7 +5373,7 @@ template <typename ClassType> void SpeculativeJIT::compileNewFunctionCommon(GPRR
 void SpeculativeJIT::compileNewFunction(Node* node)
 {
     NodeType nodeType = node->op();
-    ASSERT(nodeType == NewFunction || nodeType == NewArrowFunction);
+    ASSERT(nodeType == NewFunction || nodeType == NewArrowFunction || nodeType == NewGeneratorFunction);
     
     SpeculateCellOperand scope(this, node->child1());
 #if USE(JSVALUE64)
@@ -5410,6 +5411,8 @@ void SpeculativeJIT::compileNewFunction(Node* node)
 #else
             callOperation(operationNewArrowFunction, resultGPR, scopeGPR, executable, thisValueTagGPR, thisValuePayloadGPR);
 #endif
+        else if (nodeType == NewGeneratorFunction)
+            callOperation(operationNewGeneratorFunction, resultGPR, scopeGPR, executable);
         else
             callOperation(operationNewFunction, resultGPR, scopeGPR, executable);
         m_jit.exceptionCheck();
@@ -5417,9 +5420,10 @@ void SpeculativeJIT::compileNewFunction(Node* node)
         return;
     }
 
-    Structure* structure = nodeType == NewArrowFunction
-        ? m_jit.graph().globalObjectFor(node->origin.semantic)->arrowFunctionStructure()
-        : m_jit.graph().globalObjectFor(node->origin.semantic)->functionStructure();
+    Structure* structure =
+        nodeType == NewArrowFunction ? m_jit.graph().globalObjectFor(node->origin.semantic)->arrowFunctionStructure() :
+        nodeType == NewGeneratorFunction ? m_jit.graph().globalObjectFor(node->origin.semantic)->generatorFunctionStructure() :
+        m_jit.graph().globalObjectFor(node->origin.semantic)->functionStructure();
     
     GPRTemporary result(this);
     GPRTemporary scratch1(this);
@@ -5436,6 +5440,12 @@ void SpeculativeJIT::compileNewFunction(Node* node)
             
         addSlowPathGenerator(slowPathCall(slowPath, this, operationNewFunctionWithInvalidatedReallocationWatchpoint, resultGPR, scopeGPR, executable));
     }
+
+    if (nodeType == NewGeneratorFunction) {
+        compileNewFunctionCommon<JSGeneratorFunction>(resultGPR, structure, scratch1GPR, scratch2GPR, scopeGPR, slowPath, JSGeneratorFunction::allocationSize(0), executable, JSGeneratorFunction::offsetOfScopeChain(), JSGeneratorFunction::offsetOfExecutable(), JSGeneratorFunction::offsetOfRareData());
+
+        addSlowPathGenerator(slowPathCall(slowPath, this, operationNewGeneratorFunctionWithInvalidatedReallocationWatchpoint, resultGPR, scopeGPR, executable));
+    }
     
     if (nodeType == NewArrowFunction) {
         compileNewFunctionCommon<JSArrowFunction>(resultGPR, structure, scratch1GPR, scratch2GPR, scopeGPR, slowPath, JSArrowFunction::allocationSize(0), executable, JSArrowFunction::offsetOfScopeChain(), JSArrowFunction::offsetOfExecutable(), JSArrowFunction::offsetOfRareData());
index 2f1d76f..527374a 100644 (file)
@@ -4408,6 +4408,7 @@ void SpeculativeJIT::compile(Node* node)
 
     case NewFunction:
     case NewArrowFunction:
+    case NewGeneratorFunction:
         compileNewFunction(node);
         break;
 
index b491161..b93e869 100644 (file)
@@ -4397,6 +4397,7 @@ void SpeculativeJIT::compile(Node* node)
 
     case NewFunction:
     case NewArrowFunction:
+    case NewGeneratorFunction:
         compileNewFunction(node);
         break;
 
index d736efe..72f2200 100644 (file)
@@ -306,6 +306,7 @@ private:
             case CreateClonedArguments:
             case NewArrowFunction:
             case NewFunction:
+            case NewGeneratorFunction:
                 // Nodes that allocate get to set their epoch because for those nodes we know
                 // that they will be the newest object in the heap.
                 m_node->setEpoch(m_currentEpoch);
index e39372b..4345ff2 100644 (file)
@@ -146,6 +146,9 @@ public:
                 case NewFunction:
                     registerStructure(m_graph.globalObjectFor(node->origin.semantic)->functionStructure());
                     break;
+                case NewGeneratorFunction:
+                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->generatorFunctionStructure());
+                    break;
                     
                 default:
                     break;
index 5c3b81c..fbebb75 100644 (file)
@@ -111,6 +111,7 @@ inline CapabilityLevel canCompile(Node* node)
     case CreateActivation:
     case NewArrowFunction:
     case NewFunction:
+    case NewGeneratorFunction:
     case GetClosureVar:
     case PutClosureVar:
     case CreateDirectArguments:
index 06b5bc0..6369147 100644 (file)
@@ -52,6 +52,7 @@
 #include "FTLWeightedTarget.h"
 #include "JSArrowFunction.h"
 #include "JSCInlines.h"
+#include "JSGeneratorFunction.h"
 #include "JSLexicalEnvironment.h"
 #include "OperandsInlines.h"
 #include "ScopedArguments.h"
@@ -754,6 +755,7 @@ private:
             break;
         case NewFunction:
         case NewArrowFunction:
+        case NewGeneratorFunction:
             compileNewFunction();
             break;
         case CreateDirectArguments:
@@ -3560,34 +3562,37 @@ private:
     
     void compileNewFunction()
     {
-        ASSERT(m_node->op() == NewFunction || m_node->op() == NewArrowFunction);
-        
+        ASSERT(m_node->op() == NewFunction || m_node->op() == NewArrowFunction || m_node->op() == NewGeneratorFunction);
         bool isArrowFunction = m_node->op() == NewArrowFunction;
+        bool isGeneratorFunction = m_node->op() == NewGeneratorFunction;
         
         LValue scope = lowCell(m_node->child1());
         LValue thisValue = isArrowFunction ? lowCell(m_node->child2()) : nullptr;
         
         FunctionExecutable* executable = m_node->castOperand<FunctionExecutable*>();
         if (executable->singletonFunction()->isStillValid()) {
-            LValue callResult = isArrowFunction
-                ? vmCall(m_out.int64, m_out.operation(operationNewArrowFunction), m_callFrame, scope, weakPointer(executable), thisValue)
-                : vmCall(m_out.int64, m_out.operation(operationNewFunction), m_callFrame, scope, weakPointer(executable));
+            LValue callResult =
+                isArrowFunction ? vmCall(m_out.int64, m_out.operation(operationNewArrowFunction), m_callFrame, scope, weakPointer(executable), thisValue) :
+                isGeneratorFunction ? vmCall(m_out.int64, m_out.operation(operationNewGeneratorFunction), m_callFrame, scope, weakPointer(executable)) :
+                vmCall(m_out.int64, m_out.operation(operationNewFunction), m_callFrame, scope, weakPointer(executable));
             setJSValue(callResult);
             return;
         }
         
-        Structure* structure = isArrowFunction
-            ? m_graph.globalObjectFor(m_node->origin.semantic)->arrowFunctionStructure()
-            : m_graph.globalObjectFor(m_node->origin.semantic)->functionStructure();
+        Structure* structure =
+            isArrowFunction ? m_graph.globalObjectFor(m_node->origin.semantic)->arrowFunctionStructure() :
+            isGeneratorFunction ? m_graph.globalObjectFor(m_node->origin.semantic)->generatorFunctionStructure() :
+            m_graph.globalObjectFor(m_node->origin.semantic)->functionStructure();
         
         LBasicBlock slowPath = FTL_NEW_BLOCK(m_out, ("NewFunction slow path"));
         LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("NewFunction continuation"));
         
         LBasicBlock lastNext = m_out.insertNewBlocksBefore(slowPath);
         
-        LValue fastObject = isArrowFunction
-            ? allocateObject<JSArrowFunction>(structure, m_out.intPtrZero, slowPath)
-            : allocateObject<JSFunction>(structure, m_out.intPtrZero, slowPath);
+        LValue fastObject =
+            isArrowFunction ? allocateObject<JSArrowFunction>(structure, m_out.intPtrZero, slowPath) :
+            isGeneratorFunction ? allocateObject<JSGeneratorFunction>(structure, m_out.intPtrZero, slowPath) :
+            allocateObject<JSFunction>(structure, m_out.intPtrZero, slowPath);
         
         
         // We don't need memory barriers since we just fast-created the function, so it
@@ -3617,6 +3622,12 @@ private:
                         locations[0].directGPR(), locations[1].directGPR(),
                         CCallHelpers::TrustedImmPtr(executable), locations[2].directGPR());
                 }
+                if (isGeneratorFunction) {
+                    return createLazyCallGenerator(
+                        operationNewGeneratorFunctionWithInvalidatedReallocationWatchpoint,
+                        locations[0].directGPR(), locations[1].directGPR(),
+                        CCallHelpers::TrustedImmPtr(executable));
+                }
                 return createLazyCallGenerator(
                     operationNewFunctionWithInvalidatedReallocationWatchpoint,
                     locations[0].directGPR(), locations[1].directGPR(),
diff --git a/Source/JavaScriptCore/tests/stress/generator-function-create-optimized.js b/Source/JavaScriptCore/tests/stress/generator-function-create-optimized.js
new file mode 100644 (file)
index 0000000..9f4f409
--- /dev/null
@@ -0,0 +1,33 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+
+function *g() { }
+var GeneratorFunctionPrototype = g.__proto__;
+
+function test()
+{
+    return function *gen()
+    {
+        yield 42;
+    };
+}
+noInline(test);
+
+function test2()
+{
+    function *gen()
+    {
+        yield 42;
+    }
+
+    return gen;
+}
+noInline(test2);
+
+for (var i = 0; i < 1e4; ++i) {
+    shouldBe(test().__proto__, GeneratorFunctionPrototype);
+    shouldBe(test2().__proto__, GeneratorFunctionPrototype);
+}
diff --git a/Source/JavaScriptCore/tests/stress/generator-function-declaration-sinking-no-double-allocate.js b/Source/JavaScriptCore/tests/stress/generator-function-declaration-sinking-no-double-allocate.js
new file mode 100644 (file)
index 0000000..412bdbf
--- /dev/null
@@ -0,0 +1,36 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+var GeneratorFunctionPrototype = (function*(){}).__proto__;
+
+function call(o) { o.x = 3; }
+noInline(call);
+
+function sink (p, q) {
+    function *f() { };
+    if (p) {
+        call(f); // Force allocation of f
+        if (q) {
+            OSRExit();
+        }
+        return f;
+    }
+    return { 'x': 2 };
+}
+noInline(sink);
+
+for (var i = 0; i < 100000; ++i) {
+    var o = sink(true, false);
+    shouldBe(o.__proto__, GeneratorFunctionPrototype);
+    if (o.x != 3)
+        throw "Error: expected o.x to be 2 but is " + result;
+}
+
+// At this point, the function should be compiled down to the FTL
+
+// Check that the function is properly allocated on OSR exit
+var f = sink(true, true);
+shouldBe(f.__proto__, GeneratorFunctionPrototype);
+if (f.x != 3)
+    throw "Error: expected o.x to be 3 but is " + result;
diff --git a/Source/JavaScriptCore/tests/stress/generator-function-declaration-sinking-osrexit.js b/Source/JavaScriptCore/tests/stress/generator-function-declaration-sinking-osrexit.js
new file mode 100644 (file)
index 0000000..3be9e31
--- /dev/null
@@ -0,0 +1,30 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+var GeneratorFunctionPrototype = (function*(){}).__proto__;
+
+function sink (p, q) {
+    function *g(x) { return x; };
+    if (p) { if (q) OSRExit(); return g; }
+    function *f(x) { return x; };
+    return f;
+}
+noInline(sink);
+
+for (var i = 0; i < 10000; ++i) {
+    var f = sink(true, false);
+    shouldBe(f.__proto__, GeneratorFunctionPrototype);
+    var result = f(42);
+    if (result.next().value != 42)
+    throw "Error: expected 42 but got " + result;
+}
+
+// At this point, the function should be compiled down to the FTL
+
+// Check that the function is properly allocated on OSR exit
+var f = sink(true, true);
+shouldBe(f.__proto__, GeneratorFunctionPrototype);
+var result = f(42);
+if (result.next().value != 42)
+    throw "Error: expected 42 but got " + result;
diff --git a/Source/JavaScriptCore/tests/stress/generator-function-declaration-sinking-put.js b/Source/JavaScriptCore/tests/stress/generator-function-declaration-sinking-put.js
new file mode 100644 (file)
index 0000000..3deca57
--- /dev/null
@@ -0,0 +1,38 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+var GeneratorFunctionPrototype = (function*(){}).__proto__;
+
+function sink (p, q) {
+    function *g(x) { return x; };
+    if (p) { if (q) g.inner = 42; return g; }
+    function *f(x) { return x; };
+    return f;
+}
+noInline(sink);
+
+for (var i = 0; i < 10000; ++i) {
+    var f = sink(true, true);
+    shouldBe(f.__proto__, GeneratorFunctionPrototype);
+    var result = f(42);
+    if (result.next().value != 42)
+    throw "Error: expected 42 but got " + result;
+}
+
+// At this point, the function should be compiled down to the FTL
+
+// Test the allocation on the implicit inner else branch
+var f = sink(true, false);
+shouldBe(f.__proto__, GeneratorFunctionPrototype);
+var result = f(12);
+if (result.next().value != 12)
+    // This shouldn't matter as it should be either correct or completely crash
+    throw "Error: expected 12 but got " + result;
+
+// Check that the allocation did not sink beyond the property assignment
+var f = sink(true, true);
+shouldBe(f.__proto__, GeneratorFunctionPrototype);
+var result = f.inner;
+if (result != 42)
+    throw "Error: inner should be 42 but is " + result;
diff --git a/Source/JavaScriptCore/tests/stress/generator-function-expression-sinking-no-double-allocate.js b/Source/JavaScriptCore/tests/stress/generator-function-expression-sinking-no-double-allocate.js
new file mode 100644 (file)
index 0000000..020e6f0
--- /dev/null
@@ -0,0 +1,36 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+var GeneratorFunctionPrototype = (function*(){}).__proto__;
+
+function call(o) { o.x = 3; }
+noInline(call);
+
+function sink (p, q) {
+    var f = function *() { };
+    if (p) {
+        call(f); // Force allocation of f
+        if (q) {
+            OSRExit();
+        }
+        return f;
+    }
+    return { 'x': 2 };
+}
+noInline(sink);
+
+for (var i = 0; i < 100000; ++i) {
+    var o = sink(true, false);
+    shouldBe(o.__proto__, GeneratorFunctionPrototype);
+    if (o.x != 3)
+        throw "Error: expected o.x to be 2 but is " + result;
+}
+
+// At this point, the function should be compiled down to the FTL
+
+// Check that the function is properly allocated on OSR exit
+var f = sink(true, true);
+shouldBe(f.__proto__, GeneratorFunctionPrototype);
+if (f.x != 3)
+    throw "Error: expected o.x to be 3 but is " + result;
diff --git a/Source/JavaScriptCore/tests/stress/generator-function-expression-sinking-osrexit.js b/Source/JavaScriptCore/tests/stress/generator-function-expression-sinking-osrexit.js
new file mode 100644 (file)
index 0000000..d836581
--- /dev/null
@@ -0,0 +1,29 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+var GeneratorFunctionPrototype = (function*(){}).__proto__;
+
+function sink (p, q) {
+    var g = function *(x) { return x; };
+    if (p) { if (q) OSRExit(); return g; }
+    return function *(x) { return x; };
+}
+noInline(sink);
+
+for (var i = 0; i < 10000; ++i) {
+    var f = sink(true, false);
+    shouldBe(f.__proto__, GeneratorFunctionPrototype);
+    var result = f(42);
+    if (result.next().value != 42)
+    throw "Error: expected 42 but got " + result;
+}
+
+// At this point, the function should be compiled down to the FTL
+
+// Check that the function is properly allocated on OSR exit
+var f = sink(true, true);
+shouldBe(f.__proto__, GeneratorFunctionPrototype);
+var result = f(42);
+if (result.next().value != 42)
+    throw "Error: expected 42 but got " + result;
diff --git a/Source/JavaScriptCore/tests/stress/generator-function-expression-sinking-put.js b/Source/JavaScriptCore/tests/stress/generator-function-expression-sinking-put.js
new file mode 100644 (file)
index 0000000..43f5091
--- /dev/null
@@ -0,0 +1,37 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+var GeneratorFunctionPrototype = (function*(){}).__proto__;
+
+function sink (p, q) {
+    var g = function *(x) { return x; };
+    if (p) { if (q) g.inner = 42; return g; }
+    return function *(x) { return x; };
+}
+noInline(sink);
+
+for (var i = 0; i < 10000; ++i) {
+    var f = sink(true, true);
+    shouldBe(f.__proto__, GeneratorFunctionPrototype);
+    var result = f(42);
+    if (result.next().value != 42)
+    throw "Error: expected 42 but got " + result;
+}
+
+// At this point, the function should be compiled down to the FTL
+
+// Test the allocation on the implicit inner else branch
+var f = sink(true, false);
+shouldBe(f.__proto__, GeneratorFunctionPrototype);
+var result = f(12);
+if (result.next().value != 12)
+    // This shouldn't matter as it should be either correct or completely crash
+    throw "Error: expected 12 but got " + result;
+
+// Check that the allocation did not sink beyond the property assignment
+var f = sink(true, true);
+shouldBe(f.__proto__, GeneratorFunctionPrototype);
+var result = f.inner;
+if (result != 42)
+    throw "Error: inner should be 42 but is " + result;