@@ -989,6 +989,25 @@ BailOutRecord::RestoreValue(IR::BailOutKind bailOutKind, Js::JavascriptCallStack
989989 value = Js::JavascriptNumber::ToVar (int32Value, scriptContext);
990990 BAILOUT_VERBOSE_TRACE (newInstance->function ->GetFunctionBody (), bailOutKind, _u (" , value: %10d (ToVar: 0x%p)" ), int32Value, value);
991991 }
992+ else if (regSlot == newInstance->function ->GetFunctionBody ()->GetYieldRegister () && newInstance->function ->GetFunctionBody ()->IsCoroutine ())
993+ {
994+ // This value can only either be:
995+ // 1) the ResumeYieldData. Even though this value is on the stack, it is only used to extract the data as part of Op_ResumeYield.
996+ // So there is no need to box the value.
997+ // 2) the object used as the return value for yield statement. This object is created on the heap, so no need to box either.
998+ Assert (value);
999+
1000+ #if ENABLE_DEBUG_CONFIG_OPTIONS
1001+ if (ThreadContext::IsOnStack (value))
1002+ {
1003+ BAILOUT_VERBOSE_TRACE (newInstance->function ->GetFunctionBody (), bailOutKind, _u (" , value: 0x%p (ResumeYieldData)" ), value);
1004+ }
1005+ else
1006+ {
1007+ BAILOUT_VERBOSE_TRACE (newInstance->function ->GetFunctionBody (), bailOutKind, _u (" , value: 0x%p (Yield Return Value)" ), value);
1008+ }
1009+ #endif
1010+ }
9921011 else
9931012 {
9941013 BAILOUT_VERBOSE_TRACE (newInstance->function ->GetFunctionBody (), bailOutKind, _u (" , value: 0x%p" ), value);
@@ -1507,63 +1526,32 @@ BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptF
15071526 if (executeFunction->IsCoroutine ())
15081527 {
15091528 // If the FunctionBody is a generator then this call is being made by one of the three
1510- // generator resuming methods: next(), throw(), or return(). They all pass the generator
1511- // object as the first of two arguments. The real user arguments are obtained from the
1512- // generator object. The second argument is the ResumeYieldData which is only needed
1513- // when resuming a generator and not needed when yielding from a generator, as is occurring
1514- // here.
1529+ // generator resuming methods: next(), throw(), or return(). They all pass the generator
1530+ // object as the first of two arguments. The real user arguments are obtained from the
1531+ // generator object. The second argument is the ResumeYieldData which is only needed when
1532+ // resuming a generator and not needed when yielding from a generator, as is occurring here.
15151533 AssertMsg (args.Info .Count == 2 , " Generator ScriptFunctions should only be invoked by generator APIs with the pair of arguments they pass in -- the generator object and a ResumeYieldData pointer" );
15161534 Js::JavascriptGenerator* generator = Js::VarTo<Js::JavascriptGenerator>(args[0 ]);
15171535 newInstance = generator->GetFrame ();
15181536
1519- if (newInstance != nullptr )
1520- {
1521- // BailOut will recompute OutArg pointers based on BailOutRecord. Reset them back
1522- // to initial position before that happens so that OP_StartCall calls don't accumulate
1523- // incorrectly over multiple yield bailouts.
1524- newInstance->ResetOut ();
1525-
1526- // The debugger relies on comparing stack addresses of frames to decide when a step_out is complete so
1527- // give the InterpreterStackFrame a legit enough stack address to make this comparison work.
1528- newInstance->m_stackAddress = reinterpret_cast <DWORD_PTR>(&generator);
1529- }
1530- else
1531- {
1532- //
1533- // Allocate a new InterpreterStackFrame instance on the recycler heap.
1534- // It will live with the JavascriptGenerator object.
1535- //
1536- Js::Arguments generatorArgs = generator->GetArguments ();
1537- Js::InterpreterStackFrame::Setup setup (function, generatorArgs, true , isInlinee);
1538- Assert (setup.GetStackAllocationVarCount () == 0 );
1539- size_t varAllocCount = setup.GetAllocationVarCount ();
1540- size_t varSizeInBytes = varAllocCount * sizeof (Js::Var);
1541- DWORD_PTR stackAddr = reinterpret_cast <DWORD_PTR>(&generator); // as mentioned above, use any stack address from this frame to ensure correct debugging functionality
1542- Js::LoopHeader* loopHeaderArray = executeFunction->GetHasAllocatedLoopHeaders () ? executeFunction->GetLoopHeaderArrayPtr () : nullptr ;
1543-
1544- allocation = RecyclerNewPlus (functionScriptContext->GetRecycler (), varSizeInBytes, Js::Var);
1545-
1546- // Initialize the interpreter stack frame (constants) but not the param, the bailout record will restore the value
1547- #if DBG
1548- // Allocate invalidVar on GC instead of stack since this InterpreterStackFrame will out live the current real frame
1549- Js::Var invalidVar = (Js::RecyclableObject*)RecyclerNewPlusLeaf (functionScriptContext->GetRecycler (), sizeof (Js::RecyclableObject), Js::Var);
1550- memset (invalidVar, 0xFE , sizeof (Js::RecyclableObject));
1551- #endif
1537+ // The jit relies on the interpreter stack frame to store various information such as
1538+ // for-in enumerators. Therefore, we always create an interpreter stack frame for generator
1539+ // as part of the resume jump table, at the beginning of the jit'd function, if it doesn't
1540+ // already exist.
1541+ Assert (newInstance != nullptr );
15521542
1553- newInstance = setup.InitializeAllocation (allocation, nullptr , false , false , loopHeaderArray, stackAddr
1554- #if DBG
1555- , invalidVar
1556- #endif
1557- );
1543+ // BailOut will recompute OutArg pointers based on BailOutRecord. Reset them back
1544+ // to initial position before that happens so that OP_StartCall calls don't accumulate
1545+ // incorrectly over multiple yield bailouts.
1546+ newInstance->ResetOut ();
15581547
1559- newInstance->m_reader .Create (executeFunction);
1560-
1561- generator->SetFrame (newInstance, varSizeInBytes);
1562- }
1548+ // The debugger relies on comparing stack addresses of frames to decide when a step_out is complete so
1549+ // give the InterpreterStackFrame a legit enough stack address to make this comparison work.
1550+ newInstance->m_stackAddress = reinterpret_cast <DWORD_PTR>(&generator);
15631551 }
15641552 else
15651553 {
1566- Js::InterpreterStackFrame::Setup setup (function, args, true , isInlinee);
1554+ Js::InterpreterStackFrame::Setup setup (function, args, true /* bailedOut */ , isInlinee);
15671555 size_t varAllocCount = setup.GetAllocationVarCount ();
15681556 size_t stackVarAllocCount = setup.GetStackAllocationVarCount ();
15691557 size_t varSizeInBytes;
@@ -2826,7 +2814,7 @@ void BailOutRecord::CheckPreemptiveRejit(Js::FunctionBody* executeFunction, IR::
28262814
28272815Js::Var BailOutRecord::BailOutForElidedYield (void * framePointer)
28282816{
2829- JIT_HELPER_REENTRANT_HEADER (NoSaveRegistersBailOutForElidedYield);
2817+ JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER (NoSaveRegistersBailOutForElidedYield);
28302818 Js::JavascriptCallStackLayout * const layout = Js::JavascriptCallStackLayout::FromFramePointer (framePointer);
28312819 Js::ScriptFunction ** functionRef = (Js::ScriptFunction **)&layout->functionObject ;
28322820 Js::ScriptFunction * function = *functionRef;
0 commit comments