-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Errors in Generator Jit for SlotArray and CopyProp Restores #6700
Conversation
561d343
to
54c61a5
Compare
@nhat-nguyen this was an interesting bug - curious if you’d come across it at all? |
@@ -1792,7 +1792,7 @@ void ByteCodeGenerator::FinalizeRegisters(FuncInfo* funcInfo, Js::FunctionBody* | |||
} | |||
} | |||
|
|||
// NOTE: The FB expects the yield reg to be the final non-temp. | |||
// NOTE: The FunctionBody expects the yield reg to be the final non-temp. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ride-along comment improvement as I was tracking through where different registers go it took me a while to work out what an FB was
uint32 innerScopeCount = this->scriptFunction->GetFunctionBody()->GetInnerScopeCount(); | ||
for (uint32 i = 0; i < innerScopeCount; ++i) | ||
{ | ||
Js::RegSlot reg = this->scriptFunction->GetFunctionBody()->GetFirstInnerScopeRegister() + i; | ||
this->frame->SetNonVarReg(reg, this->frame->InnerScopeFromIndex(i)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is effectively an inverse of lines 1742-1748 of BailOut.cpp.
NOTE - getting rid of the logic in BailOut.cpp would not be a sufficient fix as it's possible for a generator to start in the interpreter and transition to the Jit at a yield point
@@ -90,4 +90,51 @@ check(gen4.next().value, 1); | |||
check(gen4.next().value, 2); | |||
check(gen4.next().value, 3); | |||
|
|||
title("Load Scope Slots in presence of for-in"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note all these cases could be done as async functions (swap yield to await) - testing only as generators as internally the important code pathways are identical and doing it with generators means the test case doesn't have to wait for promises to resolve.
00942b6
to
4b19819
Compare
@@ -5135,8 +5135,10 @@ void LinearScan::GeneratorBailIn::BuildBailInSymbolList( | |||
|
|||
if (unrestorableSymbols.TestAndClear(value->m_id)) | |||
{ | |||
if (this->NeedsReloadingSymWhenBailingIn(copyPropSym.Key())) | |||
if (this->NeedsReloadingSymWhenBailingIn(copyPropSym.Value())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compare with lines 5146-5150.
Important point is we must check if this Sym needs reloading based on its BackEndId (see line 5140 compared with line 5148.
4b19819
to
be5693a
Compare
{ | ||
BailInSymbol bailInSym(key->m_id /* fromByteCodeRegSlot */, value->m_id /* toBackendId */); | ||
bailInSymbols->PrependNode(this->func->m_alloc, bailInSym); | ||
} | ||
} | ||
else if (unrestorableSymbols.TestAndClear(key->m_id)) | ||
if (unrestorableSymbols.TestAndClear(key->m_id)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sometimes both the 'value' and 'key' appear in the unrestorableSymbols list; therefore always check both.
- copy prop'd syms can be referenced by key and value - both key and value can be in the unrestorableSymbols list - always check for both key and value - in each case check if the symbol is needed based on the BackEndId being checked 'key' or 'value'
ba708b5
to
d9e818b
Compare
a73e08c
to
d9e818b
Compare
Issue 1 - Slot Array
The SlotArray is used to store variables that belong to local scopes (
let
andconst
), on anInterpreterStackFrame
this is stored in a different location to other registers.When resuming a jitted generator/async function after a yield/await point these local scopes must be restored BUT there is no logic to handle this restoration.
Instead immediately before the function is resumed in
CallGenerator()
copy the local scopes into "normal" reg slots that the jit can read from.Notes:
ChakraCore/lib/Backend/LinearScan.cpp
Lines 5225 to 5233 in c7f192a
AND here:
https://github.com/chakra-core/ChakraCore/blob/master/lib/Backend/LinearScan.cpp#L5371
BUT was finding nothing - if the function begun in the interpreter it doesn't to write to this location, if it begun in the jit it's 0'd out when the function yields/awaits here:
ChakraCore/lib/Backend/BailOut.cpp
Lines 1742 to 1748 in c7f192a
Fix: #6678
Fix: #6679
Fix: #6692
Fix: #6690
Fix: #6697
Fix: #6698
Fix: #6699
Fix: #6705
Issue 2 - Copy prop'd syms
A book keeping error was resulting in hitting a FailFast unnecessarily when setting up a restore for a copy prop'd sym after a yield.
Also, whether or not a copy prop sym should be restored was being checked using its Key; correct if restoring to Key BUT when restoring to Value need to check based on Value.
Note, I've added the full POC from issue 6686 as a new test file because reducing it proved difficult.
Fix: #6685
Fix: #6686
@bin2415 thanks for the report
@xuelanxu thanks for the report