-
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
Enable jitting generators by default #6245
Conversation
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.
I think the flag is still useful, so could we use CONFIG_FLAG macro and change the default in configflagslist.h to true instead of removing it altogether? (difference between macros is that CONFIG_ISENABLED is always false on release build, where CONFIG_FLAG takes the default value)
Also don't unnecessarily `generateWriteBarrier` when nulling out interpreter frame (fixes Encoder issue on Mac/Linux)
Adding the below on line 1776 of BailOut.cpp removes the crash with AsyncGenerators that this PR is currently hitting: if (executeFunction->IsCoroutine() && bailOutKind != IR::BailOutForGeneratorYield)
{
Js::ResumeYieldData* resumeYieldData = static_cast<Js::ResumeYieldData*>(args[1]);
newInstance->SetNonVarReg(executeFunction->GetYieldRegister(), resumeYieldData);
} Should I open a new PR with the content of this one plus the fix, or leave for someone else to pick and incorporate? |
I'm still hoping to enable jitting generators but still have a key bug that's stopping me from doing so - I know it's been a while but please can someone who's looked at it before see if they have any suggestions? Problem comes when you have an unconditional bailout in a jitted generator (e.g. a BailOnNoProfile). In the DeadStore phase any code after that BailOnNoProfile can get deadstored - when code has been deadstored syms relating to it seem to be missed in generating the BailIn. Example: (run with function* gf()
{
for (let i = 0; i < 3; ++i)
{
yield i;
}
}
const gen = gf();
print(gen.next().value);
print(gen.next().value);
print(gen.next().value); Result:
Steps of what has happened:
|
@rhuanjl The bail-in code should work even with BailOnNoProfile (I vaguely remember having to debug some bugs similar to this); however, there were many cases that I had to handle differently, so it is likely that I might have missed something. The code that builds the bail-in symbol list is here: ChakraCore/lib/Backend/LinearScan.cpp Line 5072 in 11d9375
2 functions that determine whether a symbol needs to be reloaded when bailing in: ChakraCore/lib/Backend/LinearScan.cpp Line 5312 in 11d9375
ChakraCore/lib/Backend/LinearScan.cpp Line 5300 in 11d9375
I would start with checking whether either of the two functions correctly determines if the symbol needs reloading. It's been almost 2 years since I last worked on Chakra, so I can't remember any specific implementation; but hopefully this can help you narrow down the issue. #6183 can also be helpful when debugging bail-in code. :) |
The usual mechanism for indicating that a byte code register will be live on bailout at a point in the code where it has been optimized away is the ByteCodeUses instruction. Are you familiar with how those are generated? |
Thank you so much for the pointers @nhat-nguyen and @pleath - in the cases I'm looking at ByteCodeUses instructions are inserted for the relevant regs AND they go from jit -> interpreter when bailing out BUT they don't get loaded back when bailing in. I think with my example loop above the loop control variable gets marked as singledef when there's a BailOnNoProfile in the loop which results in the BailIn code dropping it - I'll keep digging. Also unfortunately I've found a second bug that is rather strange, if you have any thoughts I'd really appreciate it: This code crashes (AV trying to load the for-in enumerator) if it runs from the start in the JIT, but not if it does its startup yield in the interpreter. function* gf(a)
{
for (let i in arr)
{
for (let j in arr2)
{
yield i + j;
}
}
} However this code runs fine: function* gf(a)
{
print("running");
for (let i in arr)
{
for (let j in arr2)
{
yield i + j;
}
}
} The only relevant difference I can see is that in the RegAlloc phase, in the crashing case this:
Turns into this:
|
@rhuanjl Since it only crashes in the jit the very first time when loading generator for-in, I think it may be because the generator frame might have not been initialized correctly. Perhaps you can take a look at: ChakraCore/lib/Backend/IRBuilder.cpp Line 7911 in dd0d9ec
|
I had considered this - and I’ve tried pulling the frame initialisation code out of the jitted code and into JavascriptGenerator::CallGenerator. Which I think makes the logic cleaner but didn’t solve this problem. Notably this issue occurs only if these 3 factors are combined:
|
Thanks for the tips - made some progress; I think I've fixed the for-in issue, but still not solved the BailOnNoProfile issue. Though - I think I've worked out the issue, using verbose bailouts the loop control var is shown as: "BailOut: Register # 7: Constant index 0, value: 0x0001000000000000" I think the increment operation has been killed as it's after the BailOnNoProfile and so the jit has concluded that the control var is a const. I assume somewhere there is a specific optimisation I need to disable in loops containing a yield - I've spent some time hunting but can't find it. |
Superseded by #6662 Thank you for all the help. |
No description provided.