[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>
Thu, 17 Dec 2015 10:33:08 +0000 (10:33 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 17 Dec 2015 10:33:08 +0000 (10:33 +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__.

Instead of extending NewFunction / PhantomNewFunction, we just added new DFG nodes, NewGeneratorFunction and PhantomNewGeneratorFunction.
This is because NewGeneratorFunction will generate an object that has different class info from JSFunction (And if JSGeneratorFunction is extended, its size will become different from JSFunction).
So, rather than extending NewFunction with generator flag, just adding new DFG nodes seems cleaner.

Object allocation sinking phase will change NewGeneratorFunction to PhantomNewGeneratorFunction and defer or eliminate its actual materialization.
It is completely the same to NewFunction and PhantomNewFunction.
And when OSR exit occurs, we need to execute deferred NewGeneratorFunction since Baseline JIT does not consider it.
So in FTL operation, we should create JSGeneratorFunction if we see PhantomNewGeneratorFunction materialization.

* 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::convertToPhantomNewGeneratorFunction):
(JSC::DFG::Node::hasCellOperand):
(JSC::DFG::Node::isFunctionAllocation):
(JSC::DFG::Node::isPhantomFunctionAllocation):
(JSC::DFG::Node::isPhantomAllocation):
* 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):
* dfg/DFGValidate.cpp:
(JSC::DFG::Validate::validateCPS):
(JSC::DFG::Validate::validateSSA):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
(JSC::FTL::DFG::LowerDFGToLLVM::compileNewFunction):
* ftl/FTLOperations.cpp:
(JSC::FTL::operationPopulateObjectInOSR):
(JSC::FTL::operationMaterializeObjectInOSR):
* 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@194216 268f45cc-cd09-0410-ab3c-d52691b4dbfc

32 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/dfg/DFGValidate.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/ftl/FTLOperations.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 1284b71..99077c7 100644 (file)
@@ -1,3 +1,15 @@
+2015-12-17  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  Andy Estes  <aestes@apple.com>
 
         [iOS] Block loading external stylesheets in the Content-Disposition: attachment sandbox
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 249af1d..50ced27 100644 (file)
@@ -1,3 +1,114 @@
+2015-12-17  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__.
+
+        Instead of extending NewFunction / PhantomNewFunction, we just added new DFG nodes, NewGeneratorFunction and PhantomNewGeneratorFunction.
+        This is because NewGeneratorFunction will generate an object that has different class info from JSFunction (And if JSGeneratorFunction is extended, its size will become different from JSFunction).
+        So, rather than extending NewFunction with generator flag, just adding new DFG nodes seems cleaner.
+
+        Object allocation sinking phase will change NewGeneratorFunction to PhantomNewGeneratorFunction and defer or eliminate its actual materialization.
+        It is completely the same to NewFunction and PhantomNewFunction.
+        And when OSR exit occurs, we need to execute deferred NewGeneratorFunction since Baseline JIT does not consider it.
+        So in FTL operation, we should create JSGeneratorFunction if we see PhantomNewGeneratorFunction materialization.
+
+        * 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::convertToPhantomNewGeneratorFunction):
+        (JSC::DFG::Node::hasCellOperand):
+        (JSC::DFG::Node::isFunctionAllocation):
+        (JSC::DFG::Node::isPhantomFunctionAllocation):
+        (JSC::DFG::Node::isPhantomAllocation):
+        * 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):
+        * dfg/DFGValidate.cpp:
+        (JSC::DFG::Validate::validateCPS):
+        (JSC::DFG::Validate::validateSSA):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileNewFunction):
+        * ftl/FTLOperations.cpp:
+        (JSC::FTL::operationPopulateObjectInOSR):
+        (JSC::FTL::operationMaterializeObjectInOSR):
+        * 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-16  Michael Saboff  <msaboff@apple.com>
 
         ARM64 MacroAssembler improperly reuses data temp register in test32() and test8() calls
index 31b548b..1a2d72a 100644 (file)
@@ -1714,6 +1714,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomNewGeneratorFunction:
     case PhantomCreateActivation:
     case PhantomDirectArguments:
     case PhantomClonedArguments:
@@ -1760,7 +1761,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..7a06808 100644 (file)
@@ -1025,6 +1025,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case PhantomNewObject:
     case MaterializeNewObject:
     case PhantomNewFunction:
+    case PhantomNewGeneratorFunction:
     case PhantomCreateActivation:
     case MaterializeCreateActivation:
         read(HeapObjectCount);
@@ -1033,6 +1034,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..4e0c507 100644 (file)
@@ -61,6 +61,7 @@ bool clobbersExitState(Graph& graph, Node* node)
     case PhantomNewObject:
     case MaterializeNewObject:
     case PhantomNewFunction:
+    case PhantomNewGeneratorFunction:
     case PhantomCreateActivation:
     case MaterializeCreateActivation:
     case CountExecution:
@@ -77,6 +78,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..f0f16ad 100644 (file)
@@ -213,6 +213,7 @@ bool doesGC(Graph& graph, Node* node)
     case BottomValue:
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomNewGeneratorFunction:
     case PhantomCreateActivation:
     case PhantomDirectArguments:
     case PhantomClonedArguments:
@@ -247,6 +248,7 @@ bool doesGC(Graph& graph, Node* node)
     case MakeRope:
     case NewArrowFunction:
     case NewFunction:
+    case NewGeneratorFunction:
     case NewTypedArray:
     case ThrowReferenceError:
     case GetPropertyEnumerator:
index 82fa48d..13c372d 100644 (file)
@@ -1246,6 +1246,7 @@ private:
         case BooleanToNumber:
         case PhantomNewObject:
         case PhantomNewFunction:
+        case PhantomNewGeneratorFunction:
         case PhantomCreateActivation:
         case PhantomDirectArguments:
         case PhantomClonedArguments:
@@ -1387,7 +1388,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..c6c2a3a 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;
@@ -593,6 +593,16 @@ struct Node {
         children = AdjacencyList();
     }
 
+    void convertToPhantomNewGeneratorFunction()
+    {
+        ASSERT(m_op == NewGeneratorFunction);
+        m_op = PhantomNewGeneratorFunction;
+        m_flags |= NodeMustGenerate;
+        m_opInfo = 0;
+        m_opInfo2 = 0;
+        children = AdjacencyList();
+    }
+
     void convertToPhantomCreateActivation()
     {
         ASSERT(m_op == CreateActivation || m_op == MaterializeCreateActivation);
@@ -1356,6 +1366,7 @@ struct Node {
         case CheckCell:
         case NewFunction:
         case NewArrowFunction:
+        case NewGeneratorFunction:
         case CreateActivation:
         case MaterializeCreateActivation:
             return true;
@@ -1574,6 +1585,7 @@ struct Node {
         switch (op()) {
         case NewArrowFunction:
         case NewFunction:
+        case NewGeneratorFunction:
             return true;
         default:
             return false;
@@ -1584,6 +1596,7 @@ struct Node {
     {
         switch (op()) {
         case PhantomNewFunction:
+        case PhantomNewGeneratorFunction:
             return true;
         default:
             return false;
@@ -1597,6 +1610,7 @@ struct Node {
         case PhantomDirectArguments:
         case PhantomClonedArguments:
         case PhantomNewFunction:
+        case PhantomNewGeneratorFunction:
         case PhantomCreateActivation:
             return true;
         default:
index ac699e1..281cf9a 100644 (file)
@@ -272,6 +272,7 @@ namespace JSC { namespace DFG {
     macro(CheckStructureImmediate, NodeMustGenerate) \
     macro(MaterializeNewObject, NodeResultJS | NodeHasVarArgs) \
     macro(PhantomNewFunction, NodeResultJS | NodeMustGenerate) \
+    macro(PhantomNewGeneratorFunction, NodeResultJS | NodeMustGenerate) \
     macro(PhantomCreateActivation, NodeResultJS | NodeMustGenerate) \
     macro(MaterializeCreateActivation, NodeResultJS | NodeHasVarArgs) \
     \
@@ -312,6 +313,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..f6719f5 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,
@@ -1785,6 +1793,10 @@ private:
                         node->convertToPhantomNewFunction();
                         break;
 
+                    case NewGeneratorFunction:
+                        node->convertToPhantomNewGeneratorFunction();
+                        break;
+
                     case CreateActivation:
                         node->convertToPhantomCreateActivation();
                         break;
@@ -2033,7 +2045,8 @@ private:
         }
         
         case NewFunction:
-        case NewArrowFunction: {
+        case NewArrowFunction:
+        case NewGeneratorFunction: {
             Vector<PromotedHeapLocation> locations = m_locationsForAllocation.get(escapee);
             ASSERT(locations.size() == 2);
                 
index 413bf89..72218d3 100644 (file)
@@ -209,7 +209,8 @@ private:
         case GetSetter:
         case GetCallee:
         case NewArrowFunction:
-        case NewFunction: {
+        case NewFunction:
+        case NewGeneratorFunction: {
             changed |= setPrediction(SpecFunction);
             break;
         }
@@ -591,6 +592,7 @@ private:
         case BooleanToNumber:
         case PhantomNewObject:
         case PhantomNewFunction:
+        case PhantomNewGeneratorFunction:
         case PhantomCreateActivation:
         case PhantomDirectArguments:
         case PhantomClonedArguments:
index 0f456c9..a1bb6e6 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:
@@ -314,6 +315,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case ToIndexString:
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomNewGeneratorFunction:
     case PhantomCreateActivation:
     case PutHint:
     case CheckStructureImmediate:
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..626975f 100644 (file)
@@ -4408,6 +4408,7 @@ void SpeculativeJIT::compile(Node* node)
 
     case NewFunction:
     case NewArrowFunction:
+    case NewGeneratorFunction:
         compileNewFunction(node);
         break;
 
@@ -4802,6 +4803,7 @@ void SpeculativeJIT::compile(Node* node)
     case BottomValue:
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomNewGeneratorFunction:
     case PhantomCreateActivation:
     case PutHint:
     case CheckStructureImmediate:
index b491161..2facfaa 100644 (file)
@@ -4397,6 +4397,7 @@ void SpeculativeJIT::compile(Node* node)
 
     case NewFunction:
     case NewArrowFunction:
+    case NewGeneratorFunction:
         compileNewFunction(node);
         break;
 
@@ -4852,6 +4853,7 @@ void SpeculativeJIT::compile(Node* node)
     case BottomValue:
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomNewGeneratorFunction:
     case PhantomCreateActivation:
     case GetMyArgumentByVal:
     case PutHint:
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 0ab4479..de68e63 100644 (file)
@@ -455,6 +455,7 @@ private:
                 case CheckInBounds:
                 case PhantomNewObject:
                 case PhantomNewFunction:
+                case PhantomNewGeneratorFunction:
                 case PhantomCreateActivation:
                 case GetMyArgumentByVal:
                 case PutHint:
@@ -563,6 +564,7 @@ private:
                 switch (node->op()) {
                 case PhantomNewObject:
                 case PhantomNewFunction:
+                case PhantomNewGeneratorFunction:
                 case PhantomCreateActivation:
                 case PhantomDirectArguments:
                 case PhantomClonedArguments:
index 5c3b81c..32870aa 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:
@@ -194,6 +195,7 @@ inline CapabilityLevel canCompile(Node* node)
     case BottomValue:
     case PhantomNewObject:
     case PhantomNewFunction:
+    case PhantomNewGeneratorFunction:
     case PhantomCreateActivation:
     case PutHint:
     case CheckStructureImmediate:
index 507f08d..89019cb 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:
@@ -1011,6 +1013,7 @@ private:
         case ExitOK:
         case PhantomNewObject:
         case PhantomNewFunction:
+        case PhantomNewGeneratorFunction:
         case PhantomCreateActivation:
         case PhantomDirectArguments:
         case PhantomClonedArguments:
@@ -3560,34 +3563,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 +3623,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(),
index 37015b6..45c5bb9 100644 (file)
@@ -34,6 +34,7 @@
 #include "FTLLazySlowPath.h"
 #include "InlineCallFrame.h"
 #include "JSCInlines.h"
+#include "JSGeneratorFunction.h"
 #include "JSLexicalEnvironment.h"
 
 namespace JSC { namespace FTL {
@@ -92,6 +93,7 @@ extern "C" void JIT_OPERATION operationPopulateObjectInOSR(
     }
 
     case PhantomNewFunction:
+    case PhantomNewGeneratorFunction:
     case PhantomDirectArguments:
     case PhantomClonedArguments:
         // Those are completely handled by operationMaterializeObjectInOSR
@@ -160,7 +162,8 @@ extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR(
         return result;
     }
 
-    case PhantomNewFunction: {
+    case PhantomNewFunction:
+    case PhantomNewGeneratorFunction: {
         // Figure out what the executable and activation are
         FunctionExecutable* executable = nullptr;
         JSScope* activation = nullptr;
@@ -173,10 +176,10 @@ extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR(
         }
         RELEASE_ASSERT(executable && activation);
 
-        
-        JSFunction* result = JSFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);
-
-        return result;
+        if (materialization->type() == PhantomNewFunction)
+            return JSFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);
+        ASSERT(materialization->type() == PhantomNewGeneratorFunction);
+        return JSGeneratorFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);
     }
 
     case PhantomCreateActivation: {
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;